Найти в Дзене

EF Core и интеграционные тесты

Дела делаются быстро, если под руками толковый пример кода. Собственно, лежит этот пример здесь: https://github.com/dotnet/EntityFramework.Docs/blob/live/samples/core/Testing/TestingWithoutTheDatabase/SqliteInMemoryBloggingControllerTest.cs. Статья про тестирование с EF Core (точнее, про одно из направлений тестирования) - здесь: https://learn.microsoft.com/en-us/ef/core/testing/testing-without-the-database. Как мне вообще понадобились такие интеграционные тесты? Ну, есть кой-какие моменты, которые хочется прояснить на уровне тестов. Например, обработка некоторых ошибок взаимодействия с базой. А, кстати. Использую EF Core 8 и xUnit v3. Итак, создаем такой класс (все почти как в примере кода): public class EFCoreCasesIntegrationTest: IDisposable У него объявляем поля: private readonly DbConnection _connection;
private readonly DbContextOptions<FooDbContext>
_contextOptions; Далее - такие методы: private FooDbContext CreateContext() => new
FooDbContext(_contextOptions); public

Дела делаются быстро, если под руками толковый пример кода. Собственно, лежит этот пример здесь: https://github.com/dotnet/EntityFramework.Docs/blob/live/samples/core/Testing/TestingWithoutTheDatabase/SqliteInMemoryBloggingControllerTest.cs. Статья про тестирование с EF Core (точнее, про одно из направлений тестирования) - здесь: https://learn.microsoft.com/en-us/ef/core/testing/testing-without-the-database.

Как мне вообще понадобились такие интеграционные тесты? Ну, есть кой-какие моменты, которые хочется прояснить на уровне тестов. Например, обработка некоторых ошибок взаимодействия с базой.

А, кстати. Использую EF Core 8 и xUnit v3.

Итак, создаем такой класс (все почти как в примере кода):

public class EFCoreCasesIntegrationTest: IDisposable

У него объявляем поля:

private readonly DbConnection _connection;
private readonly DbContextOptions<FooDbContext>
_contextOptions;

Далее - такие методы:

private FooDbContext CreateContext() => new
FooDbContext(_contextOptions);
public void Dispose() => _connection.Dispose();

Практически копируем конструктор:

public EFCoreCasesIntegrationTest()
{
// Create and open a connection. This creates the SQLite in-memory database,
// which will persist until the connection is closed
// at the end of the test (see Dispose below).
_connection = new SqliteConnection("Filename=:memory:");
_connection.Open();
// These options will be used by the context instances in this test suite, including
// the connection opened above.
_contextOptions = new
DbContextOptionsBuilder<FooDbContext>()
.UseSqlite(_connection)
.Options;
using FooDbContext context = CreateContext();
if (!context.Database.EnsureCreated())
throw new Exception("Не удалось создать структуру БД");
}

Все! Можно писать тестовые методы. Вот мой пример:

[Theory]
[InlineData("label1")]
public void TestDoubleParallelDelete(string labelName)
{
// Arrange
using FooDbContext context1 = CreateContext();
using FooDbContext context2 = CreateContext();
context1.FooList.Add(new
Foo{Label = labelName,
InitialConnectionString = ""});
context1.SaveChanges();
// Act
Foo? foundItem = context1.FooList.Find(labelName);
Assert.NotNull(foundItem);
context1.FooList.Remove(foundItem);
foundItem = context2.FooList.Find(labelName);
Assert.NotNull(foundItem);
context2.FooList.Remove(foundItem);
context2.SaveChanges();
// Assert
try
{
context1.SaveChanges();
Assert.Fail("Здесь должно было быть исключение");
}
catch (Exception ex)
{
Assert.IsType<DbUpdateConcurrencyException>(ex, false);
Assert.False(_dbErrorAnalyzer.IsDatabaseLevelError(ex));
}
}