Skip to main content
Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
Tweeted twitter.com/StackCodeReview/status/1268965830844391424
edited tags
Link
pacmaninbw
  • 24.8k
  • 11
  • 45
  • 109
Source Link

In Memory Database in Unit tests, isolate the tests

I have stumbled across these unit tests in a code review that are using in memory db:

private DatabaseContext _context;
private Fixture _fixture;

[SetUp]
public void Setup()
{

    _fixture = new Fixture();
    _fixture.Customize(new AutoNSubstituteCustomization());

    var options = new DbContextOptionsBuilder<DatabaseContext>()
    .UseInMemoryDatabase(databaseName: "testdb")
    .Options;
    _context = new DatabaseContext(options);
}

[TearDown]
public void CleanUp()
{
    var context = _context;
    if (context == null || context.Database.ProviderName != "Microsoft.EntityFrameworkCore.InMemory")
    {
        return;
    }

    context.Database.EnsureDeleted();
    _context = null;
}

#region EmptyDB
[Test]
public void Test1()
{
    // Setup
    var logger = _fixture.Freeze<ILogger<UserRepository>>();

    var userRepo = new UserRepository(_context, logger);

    var userViews = new List<UserView>();
    userViews.AddRange(_fixture.CreateMany<UserView>(10));

    // ACT
    userRepo.UpdateUsers(userViews, CancellationToken.None).GetAwaiter().GetResult();

    // ASSERT
    Assert.AreEqual(10, _context.Users.CountAsync().GetAwaiter().GetResult());
}

[Test]
public void Test2()
{
    // Setup
    var logger = _fixture.Freeze<ILogger<UserRepository>>();

    var userRepo = new UserRepository(_context, logger);

    var identityViews = new List<IdentityView>();
    _fixture.Register<IEnumerable<UserView>>(() =>
    {
        return new UserView[] { new UserView("fish") };
    });
    userViews.AddRange(_fixture.CreateMany<UserView>(10));

    // ACT
    userRep.UpdateUsers(userViews, CancellationToken.None).GetAwaiter().GetResult();

    // ASSERT
    Assert.AreEqual(10, _context.Users.CountAsync().GetAwaiter().GetResult());
}

As you can see, the tests are using the same in memory db, which I really don't like. I also don't like the new UserRepository(_context, logger). Is it a bad practice to use the new-keyword like this?

I would prefer something like this instead:

[Test]
public void Test1()
{
    // Setup
    var provider = RegisterServices();  
    var logger = _fixture.Freeze<ILogger<UserRepository>>();
    var userRepo = provider.GetRequiredService<IUserRepository>();

    var userViews = new List<UserView>();
    userViews.AddRange(_fixture.CreateMany<UserView>(10));

    // ACT
    userRepo.UpdateUsers(userViews, CancellationToken.None).GetAwaiter().GetResult();

    // ASSERT
    Assert.AreEqual(10, _context.Users.CountAsync().GetAwaiter().GetResult());
}

private ServiceProvider RegisterServices([CallerMemberName] string memberName = "")
{
    var services = new ServiceCollection();

    services.AddDbContext<IDatabaseContext, DatabaseContext>(options =>
        options.UseInMemoryDatabase(memberName));

    services.AddPersistence("https://localhost");       

    return services.BuildServiceProvider();
}

As you can see, I have added a RegisterService method that takes the calling test as a parameter, and then uses this to create the inmemorydb. I really like this because you are isolating your tests more this way. I also think it's cleaner to read.

How would you guys do in this case? Is the first approach the way to go, or is my approach the more "right" way to do it? Or is it another better and more best practice way to do it?

I just want to know your opinions about this and about the two approaches above.