简体   繁体   中英

EF Core : Testing with InMemory Database has inconsistent behavior

I'm using the InMemory database to test my repository layer in my ASP .NET Core Web API application. Thus I have an issue, in several tests, I setup data. But, with the same code, when I run the tests sometimes the data is present and sometimes it is not. I don't understand why.

I'm using XUnit Testing Framework.

Here is my test:

public class UserRepositoryTest
    {
        private ApplicationDbContext context;

        void setup()
        {
            var options = new DbContextOptionsBuilder<ApplicationDbContext>()
                .UseInMemoryDatabase(databaseName: "ApplicationDatabase")
                .Options;

            this.context = new ApplicationDbContext(options);
            this.context.Database.EnsureDeleted();
        }

        [Fact]
        void GetUserByUsernameTest()
        {
            this.setup();
            // Given
            var manager = new User {Username = "Ombrelin", Email = "test@test.fr"};
            context.Users.Add(manager);
            context.SaveChanges();

            // When 
            var repo = new UserRepository(context);
            var user = repo.GetUserByUsername("Ombrelin");

            // Then
            Assert.Equal("Ombrelin", user.Username);
        }

        [Fact]
        void FindUsersByUsernameContainsTest()
        {
            this.setup();
            // Given
            var manager1 = new User {Username = "Arsène", Email = "test@test.fr"};
            var manager2 = new User {Username = "Jean Michel", Email = "test@test.fr"};
            context.Users.Add(manager1);
            context.Users.Add(manager2);
            context.SaveChanges();

            // When 
            var repo = new UserRepository(context);
            var users = repo.findUsersByUsernameContains("Ars");

            // Then
            Assert.Single(users);
        }

Does anyone have a clue about this?

Thanks in advance,

You are reusing the same database context across multiple tests. Tests may run in parallel. Thus, when using the same database context tests may influence each other's outcome. To avoid this, you need to isolate the tests by letting them use a clean database context:

public class UserRepositoryTest
{
    [Fact]
    public void GetUserByUsernameTest()
    {

        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: $"ApplicationDatabase{Guid.NewGuid()}")
            .Options;

        using(var context = new ApplicationDbContext(options))
        {
            // Given
            var manager = new User { Username = "Ombrelin", Email = "test@test.fr" };
            context.Users.Add(manager);
            context.SaveChanges();

            // When 
            var repo = new UserRepository(context);
            var user = repo.GetUserByUsername("Ombrelin");

            // Then
            Assert.Equal("Ombrelin", user.Username);
        }

    }
}

By appending a unique id to the database name, you are ensuring, that tests are using a unique in-memory database. Obviously, this will make test execution slower. A lot of testers also use different contexts for seeding the data and performing the test:

public class UserRepositoryTest
{
    [Fact]
    public void GetUserByUsernameTestSeparateContexts()
    {

        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: $"ApplicationDatabase{Guid.NewGuid()}")
            .Options;

        using (var context = new ApplicationDbContext(options))
        {
            // Given
            var manager = new User { Username = "Ombrelin", Email = "test@test.fr" };
            context.Users.Add(manager);
            context.SaveChanges();

        }

        using (var context = new ApplicationDbContext(options))
        {
            // When 
            var repo = new UserRepository(context);
            var user = repo.GetUserByUsername("Ombrelin");

            // Then
            Assert.Equal("Ombrelin", user.Username);
        }

    }
}

This makes the tests more realistic, since functions, that are seeding the data, and functions, that are using the data, often use different contexts. Also keep in mind, that the InMemoryProvider is not a relational database. Therefore, it does not support some features of actual database servers such as referential integrity checks, TimeStamp, IsRowVersion and others. Check the MS documentation for details: here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM