web-dev-qa-db-fra.com

xUnit.net: Configuration globale + démontage?

Cette question concerne le framework de tests unitaires xUnit.net .

Je dois exécuter du code avant l'exécution de tout test, ainsi que du code une fois tous les tests terminés. Je pensais qu'il devrait y avoir une sorte d'attribut ou d'interface marqueur pour indiquer l'initialisation globale et le code de terminaison, mais je ne les ai pas trouvés.

Sinon, si j'appelle xUnit par programme, je peux également obtenir ce que je veux avec le code suivant:

static void Main()
{
    try
    {
        MyGlobalSetup();
        RunAllTests();  // What goes into this method?
    }
    finally
    {
        MyGlobalTeardown();
    }
}

Quelqu'un peut-il m'indiquer comment exécuter de manière déclarative ou par programme un code d'installation/de démontage global?

75
Codism

Autant que je sache, xUnit ne possède pas de point d'extension d'initialisation/de démontage global. Cependant, il est facile d'en créer un. Créez simplement une classe de test de base qui implémente IDisposable et effectuez votre initialisation dans le constructeur et votre démontage dans le IDisposable.Dispose méthode. Cela ressemblerait à ceci:

public abstract class TestsBase : IDisposable
{
    protected TestsBase()
    {
        // Do "global" initialization here; Called before every test method.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Called after every test method.
    }
}

public class DummyTests : TestsBase
{
    // Add test methods
}

Toutefois, le code de configuration et de suppression de la classe de base sera exécuté pour chaque appel. Ce n'est peut-être pas ce que vous voulez, car ce n'est pas très efficace. Une version plus optimisée utiliserait le IClassFixture<T> interface pour s’assurer que la fonctionnalité globale d’initialisation/de démontage n’est appelée qu’une fois. Pour cette version, vous n’étendez pas une classe de base à partir de votre classe de test, mais vous implémentez le IClassFixture<T> interface où T fait référence à votre classe de projecteurs:

using Xunit;

public class TestsFixture : IDisposable
{
    public TestsFixture ()
    {
        // Do "global" initialization here; Only called once.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Only called once.
    }
}

public class DummyTests : IClassFixture<TestsFixture>
{
    public void SetFixture(TestsFixture data)
    {
    }
}

Cela will aura pour résultat que le constructeur de TestsFixture ne sera exécuté qu'une fois pour chaque classe testée. Cela dépend donc de ce que vous voulez exactement choisir entre les deux méthodes.

82
Erik Schierboom

Je cherchais la même réponse et, à ce moment-là, la documentation de xUnit est très utile pour implémenter les correctifs de classe et de collection qui offrent aux développeurs une large gamme de fonctionnalités d’installation/de démontage au niveau de la classe ou du groupe de classes. Ceci correspond à la réponse de Geir Sagberg et donne une bonne implémentation squelette pour illustrer ce à quoi cela devrait ressembler.

https://xunit.github.io/docs/shared-context.html

Collection Fixtures Quand utiliser: lorsque vous voulez créer un seul contexte de test et le partager entre des tests de plusieurs classes de test, et le nettoyer une fois tous les tests terminés.

Parfois, vous souhaiterez partager un objet de fixture entre plusieurs classes de test. L'exemple de base de données utilisé pour les montages de classes est un excellent exemple: vous pouvez souhaiter initialiser une base de données avec un ensemble de données de test, puis laisser ces données de test en place pour pouvoir être utilisées par plusieurs classes de test. Vous pouvez utiliser la fonctionnalité d'installation de collection de xUnit.net pour partager une instance d'objet unique entre des tests de plusieurs classes de test.

Pour utiliser les appareils de collecte, vous devez suivre les étapes suivantes:

Créez la classe de fixture et placez le code de démarrage dans le constructeur de la classe de fixture. Si la classe de fixture doit effectuer un nettoyage, implémentez IDisposable sur la classe de fixture et placez le code de nettoyage dans la méthode Dispose (). Créez la classe de définition de collection en la décorant avec l'attribut [CollectionDefinition], en lui attribuant un nom unique permettant d'identifier la collection de test. Ajoutez ICollectionFixture <> à la classe de définition de collection. Ajoutez l'attribut [Collection] à toutes les classes de test qui feront partie de la collection, en utilisant le nom unique que vous avez fourni à l'attribut [CollectionDefinition] de la classe de définition de la collection de tests. Si les classes de test ont besoin d'accéder à l'instance de la fixture, ajoutez-la en tant qu'argument de constructeur et il sera fourni automatiquement. Voici un exemple simple:

public class DatabaseFixture : IDisposable
{
    public DatabaseFixture()
    {
        Db = new SqlConnection("MyConnectionString");

        // ... initialize data in the test database ...
    }

    public void Dispose()
    {
        // ... clean up test data from the database ...
    }

    public SqlConnection Db { get; private set; }
}

[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
    // This class has no code, and is never created. Its purpose is simply
    // to be the place to apply [CollectionDefinition] and all the
    // ICollectionFixture<> interfaces.
}

[Collection("Database collection")]
public class DatabaseTestClass1
{
    DatabaseFixture fixture;

    public DatabaseTestClass1(DatabaseFixture fixture)
    {
        this.fixture = fixture;
    }
}

[Collection("Database collection")]
public class DatabaseTestClass2
{
    // ...
}

xUnit.net traite les fixtures de collection de la même manière que les fixtures de classe, à la différence que la durée de vie d'un objet fixture de collection est plus longue: il est créé avant que des tests ne soient exécutés dans l'une des classes de test de la collection et ne sera pas nettoyé. jusqu'à ce que toutes les classes de test de la collection aient fini de s'exécuter.

Les collections test peuvent également être décorées avec IClassFixture <>. xUnit.net traite cela comme si chaque classe de test de la collection de tests avait été décorée avec le projecteur de classe.

Les collections de tests ont également une influence sur la manière dont xUnit.net exécute les tests lorsqu’elles sont exécutées en parallèle. Pour plus d'informations, voir Exécution de tests en parallèle.

Remarque importante: les appareils doivent appartenir au même assemblage que le test qui les utilise.

36
Larry Smith

Il y a une solution facile facile. Utilisez le plugin Fody.ModuleInit

https://github.com/Fody/ModuleInit

C'est un paquet de nuget et quand vous l'installez, il ajoute un nouveau fichier appelé ModuleInitializer.cs au projet. Il existe une méthode statique ici qui est intégrée à l’Assembly après la construction et qui est exécutée dès que l’Assemblée est chargée et avant que tout ne soit exécuté.

J'utilise ceci pour déverrouiller la licence du logiciel dans une bibliothèque que j'ai achetée. J'oubliais toujours de déverrouiller la licence à chaque test et même d'oublier de dériver le test d'une classe de base qui le déverrouillait. Les brillantes étincelles qui ont écrit cette bibliothèque, au lieu de vous dire que sa licence était verrouillée par une licence, introduisaient de subtiles erreurs numériques qui entraînaient l’échec ou la réussite des tests. Vous ne sauriez jamais si vous avez correctement déverrouillé la bibliothèque ou non. Alors maintenant, mon module init ressemble à

/// <summary>
/// Used by the ModuleInit. All code inside the Initialize method is ran as soon as the Assembly is loaded.
/// </summary>
public static class ModuleInitializer
{
    /// <summary>
    /// Initializes the module.
    /// </summary>
    public static void Initialize()
    {
            SomeLibrary.LicenceUtility.Unlock("XXXX-XXXX-XXXX-XXXX-XXXX");
    }
}

et la licence sera déverrouillée correctement pour tous les tests placés dans cette assemblée.

11
bradgonesurfing

Pour partager le code SetUp/TearDown entre plusieurs classes, vous pouvez utiliser CollectionFixture de xUnit.

Citation:

Pour utiliser les appareils de collecte, vous devez suivre les étapes suivantes:

  • Créez la classe de fixture et placez le code de démarrage dans le constructeur de la classe de fixture.
  • Si la classe de fixture doit effectuer un nettoyage, implémentez IDisposable sur la classe de fixture et placez le code de nettoyage dans la méthode Dispose ().
  • Créez la classe de définition de collection en la décorant avec l'attribut [CollectionDefinition], en lui attribuant un nom unique permettant d'identifier la collection de test.
  • Ajoutez ICollectionFixture <> à la classe de définition de collection.
  • Ajoutez l'attribut [Collection] à toutes les classes de test qui feront partie de la collection, en utilisant le nom unique que vous avez fourni à l'attribut [CollectionDefinition] de la classe de définition de la collection de tests.
  • Si les classes de test ont besoin d'accéder à l'instance de la fixture, ajoutez-la en tant qu'argument de constructeur et il sera fourni automatiquement.
10
Geir Sagberg