2009-01-28 8 views
5

Je suis un grand fan du framework xUnit.NET; Je le trouve léger, simple, propre et extensible.Étendre xUnit.NET pour utiliser du code personnalisé lors du traitement d'une classe et de la localisation des méthodes de test

Maintenant, nous allons dire que j'ai une classe comme ceci:

public class AdditionSpecification 
{ 
    static int result; 

    public void Because() 
    { 
    result = 2 + 2; 
    } 

    public void Result_is_non_zero() 
    { 
    Assert.True(result <> 0); 
    } 

    public void Result_is_correct() 
    { 
    Assert.Equal(4, result); 
    } 
} 

Avec la classe de test ci-dessus, je veux xUnit.NET voir 2 cas de test et d'exécuter la méthode Because() avant chacun d'entre eux.

Laissant de côté toutes les questions que vous pourriez avoir avec mes noms de classe ou méthode, la structure de ce test/spécification, le cadre xUnit.NET ou BDD, voici ma question:

Comment puis-je savoir xUnit. NET que je veux personnaliser comment il identifie et exécute des méthodes de test hors de cette classe sans en utilisant un attribut personnalisé [Fact] sur chaque méthode de test cible?

Je sais que je peux dériver de BeforeAfterAttribute pour décorer chaque méthode de test avec custom avant et après l'exécution. Comment puis-je faire cela au niveau de la classe? Dois-je écrire un coureur personnalisé?

Répondre

9

Il s'avère donc que je cherchais la méthode ITestClassCommand.EnumerateTestMethods().

  1. La valeur par défaut coureur test xUnit.NET va parcourir toutes les classes votre assemblage d'essai.
  2. Pour chacun, il va vérifier un RunWithAttribute; c'est votre chance de remplacer l'implémentation ITestClassCommand qui est utilisée pour identifier les méthodes contenant des tests. (RunWithNUnit est un bon exemple)
  3. ITestClassCommand.EnumerateTestMethods() est appelée pour traiter la classe de test et renvoyer un IEnumerable de méthodes de test.
  4. chaque IMethodInfo de test est ensuite passé à ITestClassCommand.EnumerateTestCommands (IMethodInfo TestMethod) pour obtenir le IEnumerable de ITestCommands
  5. chaque ITestCommand est alors exécuté et compte tenu de la possibilité de retourner un résultat.

Dans le cas de mon exemple ci-dessus, je besoin de quelque chose comme:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 
public class RunWithMyTestClassCommandAttribute : RunWithAttribute 
{ 
    public RunWithMyTestClassCommandAttribute() 
       : base(typeof(MyTestClassCommand)) {} 
} 

Je pourrais décorer mon exemple ci-dessus:

[RunWithMyTestClassCommand] 
public class AdditionSpecification 
{ 
    static int result; 

    public void Because() 
    { 
    result = 2 + 2; 
    } 

    public void Result_is_non_zero() 
    { 
    Assert.True(result <> 0); 
    } 

    public void Result_is_correct() 
    { 
    Assert.Equal(4, result); 
    } 
} 

Enfin, dans MyTestClassCommand, je reçois à l'opportunité entre EnumerateTestMethods() et EnumerateTestCommands (IMethodInfo testMethod) d'utiliser n'importe quelle logique que je veux localiser et construire des instances ITestCommand qui sont exécutées comme tests individuels.

BTW, dans le processus de recherche de ce problème, j'ai rencontré un petit bogue dans le framework xUnit.NET où un IMethodInfo personnalisé généré par EnumerateTestMethods() n'apparaissait jamais dans EnumerateTestCommands (..) car il était en cours de déballage et réemballé par le coureur d'essai ou l'une de ses usines.

Je soumis this issue au projet xUnit sur CodePlex et il était corrected on May 30th 2009 pour xUnit.NET 1,5 CTP 2

9

La fonction IUseFixture de xUnit.net vous permet d'effectuer la configuration par appareil. Vous pouvez donc définir votre propre classe de fixation:

public class AdditionFixture : IDisposable 
{ 
    public int Because() 
    { 
    return 2 + 2; 
    } 

    public void Dispose() 
    { 
    //test tear down code 
    }  
} 

Votre classe de test peut alors mettre en œuvre cette (avec setFixture nécessitant la mise en œuvre):

public class AdditionSpecification : IUseFixture<AdditionFixture> 
{ 
    int result; 

    public void SetFixture(AdditionFixture Fixture) 
    { 
    result = Fixture.Because(); 
    } 

    [Fact] 
    public void Result_is_non_zero() 
    { 
    Assert.True(result <> 0); 
    } 

    [Fact] 
    public void Result_is_correct() 
    { 
    Assert.Equal(4, result); 
    } 
} 

Le coureur xUnit va créer une seule instance de votre appareil, et passez-le dans SetFixture avant d'exécuter chaque test. Après avoir exécuté tous vos tests, le coureur disposera alors de l'appareil s'il implémente IDisposable. J'espère que ça aide! Le wiki xUnit sur codeplex contient plus d'informations, notamment un bon exemple d'implémentation de IUseFixture pour gérer une connexion à une base de données pour vos appareils de test.

+1

Nous vous remercions d'essayer d'aider, mais cette réponse ne répond pas à la question que je posais; comment puis-je dire à xUnit.NET quelles méthodes je veux exécuter en fonction d'une convention sans utiliser l'attribut [Fact] du tout. –

+0

La solution que j'ai proposée obtient l'effet d'avoir exécuté Because() avant l'exécution de chaque test. Je réalise que j'utilise toujours l'attribut [Fact] pour y parvenir. Puis-je vous demander pourquoi vous souhaitez éviter d'utiliser [Fact] s? – BenA

+0

Pour supprimer une cérémonie de l'écriture des appareils d'essai; si je suis une convention pour coder mes montages de test alors je peux coder cette logique dans le coureur (ou dans un ITestClassCommand personnalisé et RunWithAttribute, comme cela arrive) et enlever tous les attributs [Fact], rendant ainsi mes tests un peu plus lisibles et plus rapide à coder. –

Questions connexes