2009-12-21 2 views
1

Je l'ai déjà vérifié ce .. similar question mais je ne suis pas convaincu les réponses ...Existe-t-il un moyen d'écrire des tests pour une interface, puis de la tester par rapport à toutes les classes qui implémentent le test?

J'ai actuellement une interface qui est uniquement mis en œuvre par une classe, mais qui va changer tôt ou tard. J'ai actuellement un test sur l'interface, et tous les tests au début commence par:

IFoo foo = GetConcreteFoo() 

où GetConcreteFoo est quelque chose comme

IFoo GetConcreteFoo() 
{ 
    return new ConcreteFooA(); 
} 

cependant, pourrait quand je reçois plus implémentations de Foo être un moyen de faire tout le test contre une liste de tous les différents foos de béton? Im pensant que s'il n'y a aucun moyen, au moins je peux copier/coller le test dans un nouveau fichier, avec le nom du concreteclass, et changer l'objet de retour du GetConcreteFoo ... (et en changeant le fichier d'origine (de IFooTests à IConcreteFooATests).

Je ne pense pas que cette méthode est particulièrement mauvaise .. mais son pas trop élégant/intelligent, car il serait d'exécuter le même test (fichier) contre toutes les implémentations concrètes.

est-il un moyen de faire faire?

(Im en utilisant MSTests)

Merci!

Répondre

3

Je ne suis pas sûr de MSTest, mais je crois que vous pourriez le faire dans NUnit en utilisant parameterised tests, par ex. paramétrer avec la classe d'implémentation et utiliser Activator.CreateInstance pour l'instancier. Cependant, la question plus profonde est, voudriez-vous? Vous ne dites pas à quoi ressemble votre interface, mais la raison habituelle d'avoir une interface est de permettre différentes implémentations. Par exemple, si vous avez une interface IShape avec une propriété Area, celle-ci sera implémentée différemment par Circle, Square et RorschachBlot. Qu'est-ce qu'un test pour la propriété IShape.Area pourrait affirmer de manière fiable? En général, vous pouvez donc tester de manière réaliste uniquement les classes et les méthodes (concrètes). Bien sûr, si votre interface est censée impliquer des garanties sémantiques en dehors de la spécification d'interface (par exemple Area est toujours supérieure à 0), vous pouvez tester cela pour toutes les implémentations que vous connaissez. (Pour les implémentations dont vous ne savez pas quand vous créez le test, vous devez vous fier à la communication de ces exigences supplémentaires hors de la bande, par exemple via la documentation, et faire confiance aux implémenteurs pour leur obéir. ces exigences sont plus fiables via des classes de contrat.)

+0

+1 Globalement, une bonne réponse, mais faire ce genre de test d'intégration peut aussi être utile en tant que suite de tests de régression. Une autre chose est que si vous avez une interface qui implique des «garanties sémantiques en dehors de la spécification de l'interface», c'est un candidat idéal pour une classe abstraite au lieu d'une interface. –

+0

Oui l'interface implique une garantie sémantique, puisque c'est pour un Repository où, indépendamment de l'implémentation, getAll obtient tout, Add ajoute un nouvel élément, Update mettra à jour un élément, etc. La méthode devrait toujours avoir les mêmes résultats testables, indépendamment de la mise en œuvre. Dois-je en faire une classe abstraite? Si je le fais, je dois encore tester contre toutes les implémentations. Bien que parler de tests paramétrés m'a fait réfléchir sur PEX, je vais vérifier cela aussi. –

2

Réponse courte, oui vous pouvez. Une réponse plus longue est que cela va dépendre de beaucoup de facteurs quant à la façon dont cela se passe dans votre suite de tests.

Fondamentalement, vous pouvez faire quelque chose comme ceci:

public void GenericIFooTest(IFoo testFoo) { 
    // each of your tests against foo here... 
    Assert.IsTrue(testFoo.DoesItsThing()); 
} 

Vous pouvez ensuite générer vos tests manuellement:

public void TestConcreteAFoo() { 
    IFoo aFoo = new ConcreteAFoo(param1, param2, param3); 

    GenericIFooTest(aFoo); 
} 
public void TestConcreteBFoo() { 
    IFoo bFoo = new ConcreteBFoo(); 

    GenericIFooTest(bFoo); 
} 

Ou vous pouvez utiliser la réflexion pour le faire.Cela suppose que vous savez comment instancier chaque foo dynamique, si elle a un constructeur par défaut qui serait le mieux:

public void TestAllFoos() { 
    foreach(string assembly in Directory.GetFiles(assemblyPath, "*.dll", SearchOptions.All) { 
     Assembly currentAssembly = Assembly.LoadAssembly(assembly); 

     foreach(Type internalTypes in currentAssembly.GetTypes()) { 
     if (internalTypes.IsAssignableFrom(IFoo) && !(internalTypes is IFoo)) { 
      IFoo fooType = AppActivator.CreateInstance(internalTypes); 

      GenericIFooTest(fooType); 
     } 
     } 
    } 
} 

La dernière partie est de la mémoire de quelque code que j'écrivais la semaine dernière pour faire la même chose, son un peu rude mais devrait vous aider à démarrer. Vous pouvez bien sûr utiliser LINQ pour le simplifier, je ne connais pas la syntaxe du haut de ma tête.

+0

vos deux suggestions sont de très bonnes options, je vais les essayer et les tests paramétrés pour voir ce que je me sens le plus à l'aise :) –

+0

Vous pouvez les combiner avec des tests paramétrés pour trouver un hybride, en utilisant peut-être les paramètres pour charger les assemblées ou les types qui vous intéressent. – GrayWizardx

Questions connexes