xUnit: Fact, Theory, fixtures, параллелизм
↑ 2.4 Тестирование в .NET — основы · ← Предыдущая · → Следующая
Зачем это на собесе
Спрашивают, как устроены жизненный цикл теста и общие фикстуры, и почему тесты в одном классе не идут параллельно.
Объяснение
xUnit — основной фреймворк для тестов на .NET. Для каждого тестового метода создаётся новый экземпляр класса, поэтому конструктор играет роль setup, а IDisposable.Dispose — teardown.
| Атрибут | Назначение |
|---|---|
[Fact] | обычный тест без параметров |
[Theory] + [InlineData] | параметризованный тест |
[MemberData], [ClassData] | данные из свойства или класса |
IClassFixture<T> | общий контекст на все тесты класса |
ICollectionFixture<T> + [Collection] | общий контекст на несколько классов |
IAsyncLifetime | асинхронные setup/teardown |
[Trait("cat","slow")] | категории для фильтрации |
public class PriceTests
{
[Theory]
[InlineData(100, 0.1, 90)]
[InlineData(200, 0.25, 150)]
public void Applies_discount(decimal price, decimal rate, decimal expected)
=> Assert.Equal(expected, Pricing.Discount(price, rate));
}Фикстуры
public class DbFixture : IAsyncLifetime
{
public string ConnectionString { get; private set; } = "";
public async Task InitializeAsync() { /* поднять контейнер */ }
public async Task DisposeAsync() { /* остановить */ }
}
public class RepoTests : IClassFixture<DbFixture>
{
private readonly DbFixture _db;
public RepoTests(DbFixture db) => _db = db;
}Параллелизм
Тестовые классы (коллекции) выполняются параллельно, тесты внутри класса — последовательно. Общее состояние между классами нужно изолировать либо объединить классы в одну [Collection].
Нюансы и подводные камни
- Статическое состояние делает тесты зависимыми и ломается при параллельном запуске.
async voidв тестах недопустим: используйтеasync Task.- Не полагайтесь на порядок выполнения тестов.
- Выводите диагностику через
ITestOutputHelper, а неConsole. - Для тестов на исключения:
await Assert.ThrowsAsync<T>(() => ...).
Практика
- Превратите три похожих
[Fact]в один[Theory]. - Сделайте
IClassFixtureс общей БД и очисткой данных между тестами. - Отключите параллелизм для одной коллекции и проверьте, что состояние не «протекает».
Вопросы с ответами
Сколько раз создаётся экземпляр тестового класса?
На каждый тест создаётся новый экземпляр. Общие ресурсы выносят в фикстуры.
Чем IClassFixture отличается от ICollectionFixture?
Первая — общий объект на один класс, вторая — на все классы, входящие в коллекцию.
Как xUnit распараллеливает тесты?
По тестовым коллекциям: разные классы могут идти одновременно, тесты одного класса — по очереди.
Чем xUnit отличается от NUnit и MSTest?
Новый экземпляр на тест, нет
[SetUp]/[TearDown](конструктор и Dispose), фикстуры через интерфейсы; NUnit использует один экземпляр по умолчанию и атрибуты setup.