Не удается получить обновленные данные для нескольких DbContext с одним и тем же InMemory Db - PullRequest
0 голосов
/ 15 мая 2019

Я хочу использовать базу данных InMemory для тестирования моего веб-API с помощью WebApplicationFactory.

В моем тесте я хочу настроить данные в моем dbcontext, вызвать мой API, протестировать данные. Это прекрасно работает с получением данных или созданием новых записей, но при обновлении контекст не обновляется, он по-прежнему содержит старую копию данных. Мой DbContext инициализируется с InMemoryDatabaseRoot как предложено здесь .

Вот мой WebApplicationFactory

public class InMemDbWebApplicationFactory : WebApplicationFactory<Startup>
{
    public static readonly InMemoryDatabaseRoot InMemoryDatabaseRoot = new InMemoryDatabaseRoot();

    public ServiceProvider ServiceProvider { get; private set; }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            var serviceProvider = new ServiceCollection()
                .AddEntityFrameworkInMemoryDatabase()
                .BuildServiceProvider();

            services.AddDbContext<TestDbContext>(options =>
            {
                options.UseInMemoryDatabase("InMemoryDb", InMemoryDatabaseRoot);
                options.UseInternalServiceProvider(serviceProvider);
            });

            ServiceProvider = services.BuildServiceProvider();
        });
    }
}

Мой тестовый класс использует его как IClassFixture, поэтому он используется всеми моими тестами.

Вот мой провальный тест

[Fact]
public async void TestPutBook()
{
    using (var scope = _serviceProvider.CreateScope())
    using (var context = scope.ServiceProvider.GetRequiredService<TestDbContext>())
    {
        var book1 = new Book { Name = "Book1" };
        var book2 = new Book { Name = "Book2" };
        context.Books.AddRange(book1, book2);
        context.SaveChanges();

        var updateResp = await _httpClient.PutAsJsonAsync($"/api/books/{book1.Id}", "Book1Update");
        updateResp.EnsureSuccessStatusCode();

        var getResp = await _httpClient.GetAsync($"/api/books/{book1.Id}");
        getResp.EnsureSuccessStatusCode();
        var stringResponse = await getResp.Content.ReadAsStringAsync();
        book1 = JsonConvert.DeserializeObject<Book>(stringResponse);
        Assert.NotNull(book1);
        //this works
        Assert.Equal("Book1Update", book1.Name);

        book1 = context.Books.Find(book1.Id);
        Assert.NotNull(book1);
        //this fails book1.Name is still equal to "Book1"
        Assert.Equal("Book1Update", book1.Name);
    }
}

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

Вы можете найти полное тестовое решение здесь .

1 Ответ

1 голос
/ 16 мая 2019
  1. Избегайте утилизации DbContext вручную, поскольку оно управляется контейнером зависимостей (DI) . Другими словами, не используйте using:

    using (var context = scope.ServiceProvider.GetRequiredService())
    {
         ...
    }  // the context will be <b>DISPOSED</b> now!
       // now if you try to <b>resolve the TestDbContext service</b> in future, it <b>throws</b>
       //     var context = scope.ServiceProvider.GetRequiredService(); // <b>Wrong</b>
    
  2. Тест не пройден также потому, что DbContext, используемый контроллером, и DbContext, используемый в модульном тесте, находятся в разных областях . Вот два подхода к решению этого вопроса:

    Apprach 1: Reload() вручную :

    using (var scope = _serviceProvider.CreateScope())
    {
        <b>var context = scope.ServiceProvider.GetRequiredService<TestDbContext>();</b>
        var book1 = new Book { Name = "Book1" };
        var book2 = new Book { Name = "Book2" };
        context.Books.AddRange(book1, book2);
        context.SaveChanges();
        ...
        ...
        book1 = context.Books.Find(book1.Id);
        <b>context.Entry(book1).Reload();</b> // refresh to make sure we can get the newest book1 
        Assert.NotNull(book1);
        Assert.Equal("Book1Update", book1.Name);
    }
    

    Подход 2: Создайте еще одну меньшую область действия , чтобы получить новейшую запись:

    using (var scope = _serviceProvider.CreateScope())
    {
        Book book1;
        <b>var context = scope.ServiceProvider.GetRequiredService<TestDbContext>();</b>
        book1 = new Book { Name = "Book1" };
        context.Books.AddRange(book1);
        context.SaveChanges();
        var updateResp = await _httpClient.PutAsJsonAsync($"/api/books/{book1.Id}", "Book1Update");
        updateResp.EnsureSuccessStatusCode();
        // create a new smaller scope 
        <b>using(var scope2= scope.ServiceProvider.CreateScope()){</b>
            <b>var context2 = scope2.ServiceProvider.GetRequiredService<TestDbContext>();</b>
            book1 = context2.Books.Find(book1.Id);
            Assert.NotNull(book1);
            Assert.Equal("Book1Update", book1.Name);
        }
    }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...