Я реализовал интеграционные тесты для своего API .Net Core с помощью xUnit, общих фиксаторов классов и внедрения зависимостей, чтобы я мог использовать WebApplicationFactory.
public class DatabaseFixture : WebApplicationFactory<Startup> {
public DatabaseFixture(WebApplicationFactory<Startup>) {
//Initialize Stuff
}
}
public class SaveTests : IClassFixture<DatabaseFixture> {
private DatabaseFixture databaseFixture;
public SaveTests(DatabaseFixture databaseFixture)
{
this.applicationFixture = applicationFixture;
}
//Tests.....
}
public class SaveTests2 : IClassFixture<DatabaseFixture> {
private DatabaseFixture databaseFixture;
public SaveTests2(DatabaseFixture databaseFixture)
{
this.applicationFixture = applicationFixture;
}
//Tests.....
}
Это сработало хорошо, но тестовые данные инициализируются для каждого тестового файла.
Я хочу обновить свои тесты, чтобы использовать приспособление коллекции вместо приспособления класса, однако мне не удалось реализовать приспособление коллекции при внедрении WebApplicationFactory.
[CollectionDefinition("SaveTestCollection")]
public class SaveTestCollectionFixture : ICollectionFixture<DatabaseFixture>
{
}
public class DatabaseFixture : WebApplicationFactory<Startup> {
public DatabaseFixture(WebApplicationFactory<Startup>) {
//Initialize Stuff
}
}
[Collection("SaveTestCollection")]
public class SaveTests
{
DatabaseFixture fixture;
public SaveTests(DatabaseFixture fixture)
{
this.fixture = fixture;
}
}
Я получаю следующую ошибку:
System.AggregateException : One or more errors occurred. (Collection fixture type 'DatabaseFixture' had one or more unresolved constructor arguments: WebApplicationFactory) (The following constructor parameters did not have matching fixture data: DatabaseFixture fixture)
---- Collection fixture type 'DatabaseFixture' had one or more unresolved constructor arguments: WebApplicationFactory`1 factory
---- The following constructor parameters did not have matching fixture data: DatabaseFixture fixture
Я понимаю, что внедрение зависимостей не работает, но не знаю, как это исправить.
Итак, мой вопрос: могу ли я включить WebApplicationFactory через внедрение зависимостей или мне нужно изменить способ использования WebApplicationFactory для работы с фиксаторами коллекций?
Любая помощь очень ценится





Вы хотите, чтобы инициализация данных выполнялась для каждого тестового файла. Ваш первоначальный подход верен. В противном случае вы рискуете создать общую базу данных для тестов, чего следует избегать, поскольку это может привести к поломке ваших тестов в зависимости от порядка выполнения тестов.
Пример: у вас есть интеграционный тест, который извлекает сведения об учетной записи пользователя (уже заполненной) с именем «Джон Доу». Затем у вас есть еще один тест, который удаляет учетную запись пользователя с именем «Джон Доу». Предполагая, что у вас есть общая база данных, которая заполняется только один раз для всех тестов, эти тесты будут работать, только если они выполняются в определенном порядке, то есть сначала выборка, а затем удаление. Если вы измените порядок, выборка не удастся, поскольку при удалении теста был удален ресурс, который пытался получить тест выборки.
Конечно, если ваш процесс посева занимает много времени, вы можете оптимизировать его, например. подготовьте минимальное начальное значение или разные начальные значения для разных типов тестов, потому что иногда вам не нужно заполнять всю базу данных только для того, чтобы проверить, существует ли запись в таблице. Всегда есть способ сделать это немного быстрее, и обычно оно того стоит, поскольку чем больше у вас интеграционных тестов, тем заметнее будет разница в общем времени выполнения.
Ну тогда похоже, что вы не представили всей картины. Если у вас есть конкретная организация, которая каким-то образом сводит риск, о котором я упомянул в своем ответе, к незначительному, то, возможно, вы можете попробовать подход с общей базой данных. Хотя я на собственном опыте убедился, что оно может укусить.
Рабочая демонстрация, которой вы можете следовать:
[CollectionDefinition("SaveTestCollection")]
public class SaveTestCollectionFixture : ICollectionFixture<DatabaseFixture>
{
}
public class DatabaseFixture : WebApplicationFactory<Startup>
{
}
[Collection("SaveTestCollection")]
public class SaveTests
{
private readonly DatabaseFixture _fixture;
public SaveTests(DatabaseFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task Test()
{
var client = _fixture.CreateClient();
var response = await client.GetAsync(url);
}
}
Поправьте меня, если я ошибаюсь, но здесь вообще не используется WebApplicationFactory? Похоже, что конструктор только что был удален.
Конечно, вы используете, просто не нужно использовать конструктор, DatabaseFixture сам по себе расширяет WebApplicationFactory, вы просто используете его, и этого достаточно.
В итоге было реализовано следующее решение, которое задокументировано на странице XUnit Github:
Основная коллекция для предоставления WebApplicationFactory.
[CollectionDefinition("Tests")]
public class FixtureCollection : ICollectionFixture<WebApplicationFactory<Startup>>
{
}
public class SaveTestDatabaseFixture : WebApplicationFactory<Startup>{
public static SaveTestDatabaseFixture Instance;
public TestData first_test_data { get; set; }
public SaveTestDatabaseFixture(WebApplicationFactory<Startup> factory)
{
if (Instance != null)
{
return;
}
var context = factory.GetContext();
......
first_test_data = new TestData();
context.TestData.Add(first_test_data);
context.SaveChanges();
Instance = this;
}
}
Все тесты используют статический экземпляр базы данных Fixture.
[Collection("Tests")]
public class TestDataTests : IClassFixture<SaveTestDatabaseFixture> {
private readonly SaveTestDatabaseFixture databaseFixture;
public TestDataTests (SaveTestDatabaseFixture _)
{
this.databaseFixture = SaveTestDatabaseFixture.Instance;
}
[Fact]
public async Task Test1()
{
var x = databaseFixture.TestData.Id;
//Run Test
}
}
[Collection("Tests")]
public class TestData2Tests : IClassFixture<SaveTestDatabaseFixture> {
private readonly SaveTestDatabaseFixture databaseFixture;
public TestData2Tests (SaveTestDatabaseFixture _)
{
this.databaseFixture = SaveTestDatabaseFixture.Instance;
}
[Fact]
public async Task Test2()
{
var x = databaseFixture.TestData2.Id;
//Run Test
}
}
Поскольку у меня довольно много отдельных тестовых файлов, это изменение уменьшило объем данных, сохраняемых в тестовой базе данных, на 90% (с 2000 строк до 200 строк). Это также сократило время выполнения набора тестов (включая время установки базы данных) с 53 секунд до 20 секунд.
В моем случае использования я специально хочу иметь общую базу данных. Если я хочу что-то обновить или удалить, для этой цели в базе данных сохранен определенный объект. Я хочу иметь возможность организовывать свои тесты в отдельные группы и использовать приспособления для сбора данных для однократной генерации тестовых данных для каждой группы, и чтобы каждая группа использовала один и тот же набор данных.