Сброс базы данных In-Memory между интеграционными тестами - PullRequest
0 голосов
/ 04 марта 2020

Я создал проект на основе https://github.com/jasontaylordev/CleanArchitecture. Но у меня возникают некоторые проблемы с написанием моих интеграционных тестов для контроллеров, поскольку база данных в памяти не сбрасывается между каждым тестом. Каждый тест использует WebApplicationFactory для настройки тестового веб-сервера,

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
                {
                    // Remove the app's ApplicationDbContext registration.
                    var descriptor = services.SingleOrDefault(
                        d => d.ServiceType ==
                            typeof(DbContextOptions<ApplicationDbContext>));

                    if (descriptor != null)
                    {
                        services.Remove(descriptor);
                    }

                    // Add a database context using an in-memory 
                    // database for testing.
                    services.AddDbContext<ApplicationDbContext>(options =>
                    {
                        options.UseInMemoryDatabase("InMemoryDbForTesting");
                    });

                    // Register test services
                    services.AddScoped<ICurrentUserService, TestCurrentUserService>();
                    services.AddScoped<IDateTime, TestDateTimeService>();
                    services.AddScoped<IIdentityService, TestIdentityService>();

                    // Build the service provider
                    var sp = services.BuildServiceProvider();

                    // Create a scope to obtain a reference to the database
                    // context (ApplicationDbContext).
                    using (var scope = sp.CreateScope())
                    {
                        var scopedServices = scope.ServiceProvider;
                        var context = scopedServices.GetRequiredService<ApplicationDbContext>();
                        var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                        // Ensure the database is created.
                        context.Database.EnsureCreated();

                        try
                        {
                            // Seed the database with test data.
                            SeedSampleData(context);
                        }
                        catch (Exception ex)
                        {
                            logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {Message}", ex.Message);
                        }
                    }
                })
                .UseEnvironment("Test");
        }
    ...

Где следующий тест создания:

namespace CleanArchitecture.WebUI.IntegrationTests.Controllers.TodoItems
{
    public class Create : IClassFixture<CustomWebApplicationFactory<Startup>>
    {
        private readonly CustomWebApplicationFactory<Startup> _factory;

        public Create(CustomWebApplicationFactory<Startup> factory)
        {
            _factory = factory;
        }

        [Fact]
        public async Task GivenValidCreateTodoItemCommand_ReturnsSuccessCode()
        {
            var client = await _factory.GetAuthenticatedClientAsync();

            var command = new CreateTodoItemCommand
            {
                Title = "Do yet another thing."
            };

            var content = IntegrationTestHelper.GetRequestContent(command);

            var response = await client.PostAsync($"/api/todoitems", content);

            response.EnsureSuccessStatusCode();
        }
    ...

И следующий тест чтения:

namespace CleanArchitecture.WebUI.IntegrationTests.Controllers.TodoItems
{
    public class Read: IClassFixture<CustomWebApplicationFactory<Startup>>
    {
        private readonly CustomWebApplicationFactory<Startup> _factory;

        public Read(CustomWebApplicationFactory<Startup> factory)
        {
            _factory = factory;
        }

        [Fact]
        public async Task ShouldRetriveAllTodos()
        {
            var client = await _factory.GetAuthenticatedClientAsync();

            var response = await client.GetAsync($"/api/todoitems");

            var todos = Deserialize(response);
            todos.Count().Should().Be(1); //but is 2 because database is shared between read and create test class.
        }

Проблема заключается в том, что база данных в памяти не сбрасывается между каждым тестом. Я попытался сгенерировать другое имя для базы данных в памяти, используя Guid.New.ToString, но затем тесты не находят заполненные данные базы данных, и, поместив тесты в одну коллекцию XUnit, безрезультатно.

Есть хорошие идеи, как сделать так, чтобы тесты не разделяли базу данных?

1 Ответ

1 голос
/ 04 марта 2020

Мы изолируем тест друг от друга следующим образом при создании DbContext:

private static DbContextOptions<ApplicationDbContext> CreateDbContextOptions()
{
    return new DbContextOptionsBuilder<ApplicationDbContext>()
        //Database with same name gets reused, so let's isolate the tests from each other...
        .UseInMemoryDatabase(Guid.NewGuid().ToString()) 
        .Options;
}

В классе теста:

using (var context = new ApplicationDbContext(DbContextTestHelper.CreateDbContextOptions())
{
    //arrange data in context

    var testee = new XY(context);

    //do test

    //do asserts
}
...