Home > OS >  Entity Framework In Memory Database With Tests Fail Only When Running Tests In Series
Entity Framework In Memory Database With Tests Fail Only When Running Tests In Series

Time:06-29

My Repository Tests use an in memory database that clears the context after each test run:

using System;
using System.Threading.Tasks;
using Application;
using Microsoft.EntityFrameworkCore;
using NUnit.Framework;

namespace UnitTests.Repositories;

public class RepositoryTestBase
{
    protected readonly ApplicationDbContext _context;
    
    public RepositoryTestBase()
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: "ApplicationDbContext").Options;

        _context = new ApplicationDbContext(options);
    }
    
    [TearDown]
    public async Task AfterEach()
    {
        _context.ChangeTracker.Clear();
        await _context.Database.EnsureDeletedAsync();
        await _context.Database.EnsureCreatedAsync();
    }

    [OneTimeTearDown]
    public void AfterAll()
    {
        _context.Dispose();
    }
}

Here is the repository test that is failing, again if I run this test on its own it passes, and only fails when running all of my repository tests together:

[Test]
public async Task ReplaceItemTags_EmptyFirst()
{
    var originalTags = TestData.AlexItemA.Tags;
    await _context.Items.AddAsync(TestData.AlexItemA);
    await _unitOfWork.CompleteAsync();

    var result = await _ItemRepository.ReplaceItemTags(TestData.AlexItemA.Id, new List<Tag>() {TestData.TagB});

    //TESTS FAIL HERE, THIS IS A WRAPPER AROUND _context.SaveChangesAsync()
    await _unitOfWork.CompleteAsync();
    
    Assert.NotNull(result);
    Assert.AreNotEqual(originalTags, result?.Tags);
    Assert.AreEqual(1, result?.Tags?.Count);
    Assert.Contains(TestData.TagB, result?.Tags?.ToList());
}

Here is the method that is being tested:

    public async Task<Item?> ReplaceItemTags(Guid ItemId, List<Tag> tags)
    {
        var Item = await _context.Items.FindAsync(ItemId);
        if (Item == null)
        {
            return null;
        }
        Item.Tags = tags.Distinct().ToList();
        return Item;
    }

Here is the stack trace from the test failure:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException : Attempted to update or delete an entity that does not exist in the store.
   at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryTable`1.Update(IUpdateEntry entry)
   at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryStore.ExecuteTransaction(IList`1 entries, IDiagnosticsLogger`1 updateLogger)
   at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryDatabase.SaveChangesAsync(IList`1 entries, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Application.Repositories.UnitOfWork.CompleteAsync() in C:\src\EasyForum.API\Application\Repositories\UnitOfWork.cs:line 20
   at UnitTests.Repositories.ItemRepositoryTests.ReplaceItemTags_EmptyFirst() in C:\src\EasyForum.API\UnitTests\Repositories\ItemRepositoryTests.cs:line 226
   at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.GetResult()
   at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(Func`1 invoke)
   at NUnit.Framework.Internal.Commands.TestMethodCommand.RunTestMethod(TestExecutionContext context)
   at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
   at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
   at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)

I think it may have to do with my static TestData class. Somehow the tracking on the instances of my models are not getting reset. I have tried to detatch all tracking at teardown as well via the following code:

    foreach (var entity in _context.ChangeTracker.Entries())
    {
        entity.State = EntityState.Detached;
    }

If anyone has any ideas please let me know. Perhaps there is a better way to implement an in memory database for my testing purposes.

CodePudding user response:

Found the solution.

Tags are a database entity, I need to make sure they are stored in the database before adding them as a foreign key in the Item.

  • Related