xUnit.net: глобальная настройка + демонтаж?


этот вопрос касается структуры модульного тестирования xUnit.net.

Мне нужно запустить некоторый код перед выполнением любого теста, а также некоторый код после выполнения всех тестов. Я думал, что должен быть какой-то атрибут или интерфейс маркера, чтобы указать глобальный код инициализации и завершения, но не смог их найти.

кроме того, если я вызываю xUnit программно, я также могу достичь того, что я хочу со следующим код:

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

может ли кто-нибудь дать мне подсказку о том, как декларативно или программно запустить какой-либо глобальный код установки/демонтажа?

4 52

4 ответа:

насколько я знаю, xUnit не имеет глобальной точки расширения инициализации/демонтажа. Однако его легко создать. Просто создайте базовый тестовый класс, который реализует IDisposable и сделать вашу инициализацию в конструкторе и ваш демонтаж в IDisposable.Dispose метод. Это будет выглядеть так:

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
}

тем не менее, установка базового класса и код демонтажа будут выполняться для каждого вызова. Это может быть не то, что вы хотите, так как это не очень эффективно. Более оптимизированная версия будет использовать IClassFixture<T> интерфейс, чтобы гарантировать, что глобальная функция инициализации/демонтажа вызывается только один раз. Для этой версии вы не расширяете базовый класс из своего тестового класса, но реализуете IClassFixture<T> интерфейс, где T ссылается на ваш класс приспособления:

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)
    {
    }
}

этой будет результат в конструкторе TestsFixture только один раз для каждого тестируемого класса. Таким образом, это зависит от того, что вы хотите точно выбрать между двумя методами.

Я искал тот же ответ, и в это время документация xUnit очень полезна в отношении того, как реализовать светильники класса и светильники коллекции, которые дают разработчикам широкий спектр функций настройки/демонтажа на уровне класса или группы классов. Это согласуется с ответом от Гейра Sagberg, и дает хорошую реализацию скелет, чтобы проиллюстрировать, как оно должно выглядеть.

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

Светильники Коллекции Когда использовать: когда вы хотите создать один тестовый контекст и разделить его между тестами в нескольких тестовых классах и очистить его после завершения всех тестов в тестовых классах.

иногда вы захотите разделить объект fixture между несколькими тестовыми классами. Пример базы данных, используемый для светильников класса, является отличным примером: вы можете захотеть чтобы инициализировать базу данных с набором тестовых данных, а затем оставить эти тестовые данные на месте для использования несколькими тестовыми классами. Вы можете использовать особенность приспособления собрания xUnit.net совместное использование одного экземпляра объекта между тестами в нескольких тестовых классах.

для использования коллекционных светильников необходимо выполнить следующие действия:

создать класс приспособления, и поставить загрузочный код в приспособление конструктор класса. Если класс приспособления должен выполнить очистку, реализуйте IDisposable в классе fixture и поместите код очистки в метод Dispose (). Создайте класс определения коллекции, украсив его атрибутом [CollectionDefinition] и присвоив ему уникальное имя, которое будет идентифицировать тестовую коллекцию. Добавьте ICollectionFixture в класс определения коллекции. Добавьте атрибут [Collection] во все тестовые классы, которые будут частью коллекции, используя уникальное имя, предоставленное классу определения тестовой коллекции [CollectionDefinition] атрибут. Если тестовые классы нуждаются в доступе к экземпляру fixture, добавьте его в качестве аргумента конструктора, и он будет предоставлен автоматически. Вот простой пример:

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 относится к элементам коллекции во многом так же, как и к элементам класса, за исключением того, что срок службы объекта элемента коллекции больше: он создается до запуска тестов в любом из тестовых классов в коллекции и не будет очищен пока все тестовые классы в коллекции не закончат работу.

тестовые коллекции также могут быть украшены IClassFixture. xUnit.net относится к этому так, как если бы каждый отдельный тестовый класс в тестовой коллекции был украшен креплением класса.

тестовые коллекции также влияют на способ xUnit.net запускает тесты при их параллельном выполнении. Дополнительные сведения см. В разделе параллельное выполнение тестов.

важное примечание: светильники должны быть в том же сборка как тест, который их использует.

есть простое решение. Используй Фоди.Модульный плагин

https://github.com/Fody/ModuleInit

это пакет nuget, и при его установке он добавляет новый файл под названием ModuleInitializer.cs в проект. Здесь есть один статический метод, который вплетается в сборку после сборки и запускается, как только сборка загружается и перед запуском чего-либо.

Я использую это, чтобы открыть лицензионное программное обеспечение для библиотеки, которая у меня есть приобретенный. Я всегда забывал разблокировать лицензию в каждом тесте и даже забыл вывести тест из базового класса, который его разблокирует. Яркие искры, которые написали эту библиотеку, вместо того, чтобы сказать вам, что она была заблокирована лицензией, ввели тонкие числовые ошибки, которые заставляют тесты терпеть неудачу или проходить, когда они не должны. вы никогда не узнаете, правильно ли вы разблокировали библиотеку или нет. Так что теперь мой модуль init выглядит как

/// <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");
    }
}

и все тесты, которые помещаются в эта сборка будет иметь лицензию разблокирована правильно для них.

поделиться установки/демонтажа-код между несколькими классами, можно использовать xUnit это CollectionFixture.

цитата:

для использования коллекционных светильников необходимо выполнить следующие действия:

  • создать класс приспособления, и поставить загрузочный код в приспособление конструктор класса.
  • если класс приспособления должен выполнить очистку, то снабдите IDisposable на классе приспособления, и положите код чистки внутри метод Dispose() метод.
  • создайте класс определения коллекции, украсив его атрибутом [CollectionDefinition], присвоив ему уникальное имя, которое будет определите набор тестов.
  • добавить ICollectionFixture в класс определения коллекции.
  • добавьте атрибут [Collection] во все тестовые классы, которые будут частью коллекции, используя уникальное имя, предоставленное тесту определение семейства классов [CollectionDefinition] атрибут.
  • если тестовые классы нуждаются в доступе к экземпляру fixture, добавьте его в качестве аргумента конструктора, и он будет предоставлен автоматически.