Подтвердить что ты не робот

XUnit.net: Глобальная настройка + срыв?

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

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

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

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

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

4b9b3361

Ответ 1

Насколько я знаю, 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 DummyTests(TestsFixture data)
    {
    }
}

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

Ответ 2

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

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

Приспособления коллекции Когда использовать: когда вы хотите создать один контекст теста и поделиться им среди тестов в нескольких классах тестов, и очистить его после завершения всех тестов в классах тестов.

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

Чтобы использовать коллекционные светильники, вам необходимо выполнить следующие шаги:

Создайте класс fixture и поместите код запуска в конструктор класса fixture. Если классу фикстур необходимо выполнить очистку, реализуйте IDisposable для класса фикстуры и поместите код очистки в метод 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 выполняет тесты при их параллельном запуске. Для получения дополнительной информации см. Запуск тестов параллельно.

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

Ответ 3

Существует простое решение. Используйте плагин Fody.ModuleInit

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

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

Ответ 4

Чтобы разделить SetUp/TearDown-код между несколькими классами, вы можете использовать xUnit CollectionFixture.

Цитата:

Чтобы использовать инструменты коллекции, вам необходимо выполнить следующие шаги:

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

Ответ 5

Должна быть опция bootstrap для запуска кода перед чем-либо еще. По крайней мере, для PHPUnit. Вы не указали, какие рамки вы используете.

Я не знаю, как запустить код после всех тестов. Но вы можете сделать это в обертке script, которая сначала запускает тесты, а затем запускает все, что вам нужно.