2008-09-20 7 views
103

Nous mettons tous nos tests unitaires dans leurs propres projets. Nous trouvons que nous devons rendre certaines classes publiques au lieu d'internes juste pour les tests unitaires. Est-il de toute façon pour éviter d'avoir à faire cela. Quelles sont les implications de la mémoire en rendant les classes publiques au lieu d'être scellées?Rendre le code interne mais disponible pour les tests unitaires d'autres projets

+2

possible duplicata de [C# "internal" modi d'accès fier lors des tests unitaires] (http://stackoverflow.com/questions/358196/c-sharp-internal-access-modifier-when-doing-unit-testing) –

Répondre

160

Si vous utilisez .NET, l'attribut d'assemblage InternalsVisibleTo vous permet de créer des assemblages "amis". Ce sont des assemblys spécifiques fortement nommés qui sont autorisés à accéder aux classes internes et aux membres de l'autre assembly. Notez que ceci doit être utilisé avec discrétion car il couple étroitement les assemblages impliqués. Une utilisation courante pour InternalsVisibleTo est pour les projets de tests unitaires. Ce n'est probablement pas un bon choix à utiliser dans vos assemblages d'applications réels, pour la raison indiquée ci-dessus.

+0

Merci! Cela devrait certainement être la réponse acceptée. – fresskoma

+14

Je suggérerais de mettre un DEBUG #if autour de l'attribut, puis un test unitaire dans le débogage. De cette façon, vous serez sûr que l'attribut n'est pas défini sur le code de version. –

+0

Ceci est juste une idée, je ne sais pas .... Que diriez-vous: #if DEBUG classe publique IniReader #else classe interne IniReader #endif Probablement pas recommandé? Pourquoi? – jmelhus

-2

Les classes peuvent être à la fois publiques et scellées.

Mais, ne faites pas cela.

Vous pouvez créer un outil pour réfléchir sur des classes internes et émettre une nouvelle classe qui accède à tout via la réflexion. MSTest le fait. Editer: Je veux dire, si vous ne voulez pas inclure de matériel de test dans votre assemblage d'origine; Cela fonctionne également si les membres sont privés.

+1

Attendez, quoi? Vous dites ne pas faire une «classe scellée publique»? Quel est votre raisonnement pour cette gemme? – crush

5

S'il s'agit d'une classe interne, il ne doit pas être utilisé de manière isolée. Par conséquent, vous ne devriez pas vraiment le tester en dehors de tester une autre classe qui utilise cet objet en interne.

Tout comme vous ne devriez pas tester les membres privés d'une classe, vous ne devriez pas tester les classes internes d'une DLL. Ces classes sont des détails de mise en œuvre de certaines classes accessibles au public, et devraient donc être bien exercées à travers d'autres tests unitaires. L'idée est que vous ne voulez tester le comportement d'une classe que parce que si vous testez des détails d'implémentation internes, vos tests seront fragiles. Vous devriez être capable de changer les détails d'implémentation de n'importe quelle classe sans casser tous vos tests.

Si vous trouvez que vous avez vraiment besoin de tester cette classe, vous pouvez réexaminer pourquoi cette classe est interne en premier lieu.

+0

Les détails de mise en œuvre doivent être utilisés dans le cadre d'un test englobant. Ne jetez pas un coup d'œil aux variables privées ... testez le comportement attendu. Si le test est correct, tous les tuyaux et câbles internes doivent être testés. Voté – Gishu

+61

je ne suis pas nécessairement d'accord avec cela comme ces classes sont "publiques" à d'autres classes à l'intérieur de la DLL et la fonctionnalité de la classe doit être testée indépendamment – leora

+22

Je ne suis pas d'accord non plus. Les unités sont des unités et doivent être testées isolément. – Sentinel

3

à des fins de documentation

Sinon, vous pouvez instancier classe interne en utilisant Type.GetType méthode

exemple

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly; 
//Namespace.ServiceWrapper is internal 
var type = asm.GetType("Namespace.ServiceWrapper"); 
return (IServiceWrapper<T>)Activator 
    .CreateInstance(type, new object[1] { /*constructor parameter*/ }); 

pour le type générique il existe différents processus comme ci-dessous:

var asm = typeof(IServiceWrapper).Assembly; 
//note the name Namespace.ServiceWrapper`1 
//this is for calling Namespace.ServiceWrapper<> 
var type = asm.GetType("Namespace.ServiceWrapper`1"); 
var genType = type.MakeGenericType(new Type[1] { typeof(T) }); 
return (IServiceWrapper<T>)Activator 
    .CreateInstance(genType, new object[1] { /*constructor parameter*/});