Testcontainers .net 8 db context seems to not be updated in [Fact], solved the issue, looking for explanation

I’m trying to implement an integration test on the Put endpoint of a crud controller


tl;dr;

Needed to fetch without tracking, but why? Tests and fixture don’t share context? Shouldn’t they?


[HttpPut("{id}")]
    public async Task<IActionResult> Put([FromRoute] string id, [FromBody] UpdateUser updateUser, [FromServices] CaManDbContext dbContext, CancellationToken cancellationToken)
    {
        if (!Ulid.TryParse(id, out var ulId))
        {
            return BadRequest();
        }
        
        var existingUser = await dbContext.Users
            .Include(u => u.ContactInfo)
            .FirstOrDefaultAsync(u => u.Id == new UserId(ulId), cancellationToken);

        if (existingUser is null)
        {
            return NotFound();
        }

        if (!string.IsNullOrWhiteSpace(updateUser.shortName))
        {
            var newShortName = ShortName.Create(updateUser.shortName);

            existingUser.UpdateShortName(newShortName);
        }

        if (!string.IsNullOrWhiteSpace(updateUser.email))
        {
            var newEmail = Email.Create(updateUser.email);

            existingUser.UpdateEmail(newEmail);
        }

        await dbContext.SaveChangesAsync(cancellationToken);

        return Ok(existingUser);
    }

I’ve already implemented the api fixture and tests on other endpoints run smoothly.


However, the following test

[Fact]
    public async Task Update_ShouldUpdate_EmailOfExistingUser_ToDatabase()
    {
        // Arrange
        var existingUser = await UserHelperMethods.CreateRandomUserInDb(_apiDbContext);
        
        var shortName = existingUser.ShortName.Value;
        var email = "[email protected]";

        // Act
        var httpResponse =
            await _apiClient.PutAsJsonAsync($"/api/Users/{existingUser.Id.Value}", 
                new UpdateUser(null, email, null));

        Assert.True(httpResponse.IsSuccessStatusCode);
        
        var updatedUser = await httpResponse.Content.ReadFromJsonAsync<CreatedTestUser>();

        //Assert
        Assert.NotNull(updatedUser);
        Assert.Equal(existingUser.Id, updatedUser.Id);
        Assert.Equal(shortName, updatedUser.ShortName.Value);
        Assert.Equal(email, updatedUser.Email.Value);
        
        var fetchedUser = await (await _apiClient.GetAsync($"api/Users/{existingUser.Id.Value}")).Content.ReadFromJsonAsync<CreatedTestUser>();
        
        Assert.NotNull(fetchedUser);
        Assert.Equal(updatedUser.Id, fetchedUser.Id);
        Assert.Equal(updatedUser.ShortName.Value, fetchedUser.ShortName.Value);
        Assert.Equal(updatedUser.Email.Value, fetchedUser.Email.Value);

        var dbUser = await _apiDbContext.Users.FirstOrDefaultAsync(u => u.Id == updatedUser.Id);
        
        Assert.NotNull(dbUser);
        Assert.Equal(updatedUser.Id, dbUser.Id);
        Assert.Equal(updatedUser.ShortName.Value, dbUser.ShortName.Value);
        Assert.Equal(updatedUser.Email.Value, dbUser.Email.Value);
    }

fails on the last line Assert.Equal(updatedUser.Email.Value, dbUser.Email.Value);

The failure message indicates that even though the request is processed correctly, the user inside the db context is not updated!


The api factory:

public class IntegrationTestApiFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
    private readonly MySqlContainer 
        _dbContainer = new MySqlBuilder()
            .WithImage("mysql:8.0")
            .Build();

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            var dbDescriptor = services
                .SingleOrDefault(s => s.ServiceType == typeof(DbContextOptions<CaManDbContext>));

            if (dbDescriptor is not null)
            {
                services.Remove(dbDescriptor);
            }

            services.AddDbContext<CaManDbContext>(optionsBuilder =>
            {
                var serverVersion = new MySqlServerVersion(new Version(8, 0, 36));
                optionsBuilder.UseMySql(_dbContainer.GetConnectionString(), serverVersion);
            });
        });
    }

    public async Task InitializeAsync()
    {
        await _dbContainer.StartAsync();
        using var scope = Services.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<CaManDbContext>();
        await dbContext.Database.MigrateAsync();
    }

    public new Task DisposeAsync()
    {
        return _dbContainer.StopAsync();
    }
}

and the integration test base class;

public abstract class BaseIntegrationTest : IClassFixture<IntegrationTestApiFactory>
{
    protected readonly HttpClient _apiClient;
    protected readonly CaManDbContext _apiDbContext;
    protected readonly IServiceScope _apiScope;
    
    protected BaseIntegrationTest(IntegrationTestApiFactory apiFactory)
    {
        _apiClient = apiFactory.Server.CreateClient();
        _apiScope = apiFactory.Services.CreateScope();
        _apiDbContext = _apiScope.ServiceProvider.GetRequiredService<CaManDbContext>();
    }
}

The whole code can be found here which can directly run the tests, also here can be found the failed test run on github’s action (the failure is the same as in local environment)

I tried calling the db context from different location in order to clean up anything persistent but got nowhere

Solved the issue but still would like an explanation of why it’s happening.

The solution is to add AsNotTracking() while fetching the updated user, I guess to avoid reaching to the cached entity of the scoped context.

Replace

var dbUser = await _apiDbContext.Users.FirstOrDefaultAsync(u => u.Id == updatedUser.Id);

with

var dbUser = await _apiDbContext.Users.AsNoTracking().FirstOrDefaultAsync(u => u.Id == updatedUser.Id);

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật