Search code examples
c#unit-testingentity-framework-corexunitin-memory-database

Why are separate DbContexts modifying the same DbSet during Unit Tests?


Enviornment:

Xunit 2.3.1

Microsoft.EntityFrameworkCore.InMemory 2.1.0

I have three test methods in three different classes that when run separately pass but when run together only one will pass and the other two will fail. Then when the two are run again - one will pass and the other will fail.

I've found this issues, I'm just not sure why it is happening.

I'll show some code and below the code are design explanations.

The gist:

Three test classes:

public class FirstReviewRepoTest
public class SecondReviewRepoTest
public class ThirdReviewRepoTest

The test methods(They are all name the same):

public void GetReviewDataTest(){}
public void GetReviewDataTest(){}
public void GetReviewDataTest(){}

See some more code below of one of the tests fleshed out:

[Trait("Repo","")]
public class FirstReviewRepoTest{

[Fact(DisplayName = "GetReviewDataTest")]
[Trait("Repo","FirstReviewRepo")]
pubic void GetReviewDataTest(){
var options = new DbContextOptionsBuilder<MyAppDbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;

using (MyAppDbContext context = new MyAppDbContext(options)){
    SeedDb(context);
    AddData(context);
    FirstReviewRepository repository = new FirstReviewRepository(context);

    FirstReviewDM dm = repository.GetReviewData(context.ItemInstanceDocumentInstance.Last().Id);
    FirstReviewDM expected = new FirstReviewDM{
       Analysis = "Analysis",
       Comments = "Comments",
       Solution = "Solution",
       Review = "Review",
       ReviewerToApproved = new KeyValuePair<string,bool>
}

Assert.Equal(expected,dm);
}

private void SeedDb(MyAppDbContext){
     CreateDocumentDescriptions(context);
     CreateTaskDescriptions(context);
     CreateDocumentFieldDescriptions(context);
     //... These go on...

     AddUsersWithRoleData(context); //This is where the issue begins
     context.SaveChanges();
}

private void AddUsersWithRoleData(MyAppDbContext context){
     List<Users> users = new List<Users>()
     {
         new Users { Name = "user1" },
         new Users { Name = "user2" },
         new Users { Name = "user3" },
         new Users { Name = "user4" },
         new Users { Name = "user5" },
         new Users { Name = "user6" },
         new Users { Name = "user7" },
         new Users { Name = "user8" }
{
}

Design Explanations:

Originally the test classes all inherited from a base class that defined SeedDb(MyAppDbContext) and all its containing methods. The base class also had a method called GetContextWithData() that returned a new MyAppDbContext which had it's tables populated via SeedDb()

I moved it all out of there for concern inheritance was messing something up.

The issue:

All three test methods create a repository object and call a method to get the respective review data. In those methods a call is made to the Users dbSet e.g. _context.Users.Single(x=> x.Id == document.AuthorId). In all three of the tests document.AuthorId is equal to 5. Somehow all three calls of AddUsersWithRoleData are adding users to seemingly the same DbSet<Users>

So, only one DbSet has user with Id = 5.

I've tried:

Making the tests run in order. Doesn't work

Declaring Ids for the users when added to the DbContext. Fixes this issue but breaks other tests that add Users to the DbSet


Solution

  • The reason the Ids are not resetting is because InMemory Databases in EfCore.InMemory(2.1) share key generated values. The reasoning behind this is for them to be Db agnostic.

    This discussion can be found in depth here