内存数据库中的实体框架仅在串联运行测试时才失败

发布于 2025-02-10 15:43:14 字数 4279 浏览 6 评论 0原文

我的存储库测试使用一个在内存数据库中清除每个测试后清除上下文:

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();
    }
}

这是存储库测试失败,如果我自己运行此测试,则仅在运行所有存储库测试时才失败:

[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());
}

这是正在测试的方法:

    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;
    }

这是测试失败的堆栈跟踪:

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)

我认为这可能与我的静态testdata类有关。不知何故,对我的模型实例的跟踪没有重置。我试图通过以下代码在拆卸时进行所有跟踪:

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

如果有人有任何想法,请告诉我。也许有一种更好的方法来实现记忆数据库以进行我的测试目的。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

给妤﹃绝世温柔 2025-02-17 15:43:14

找到了解决方案。

标签是数据库实体,我需要确保将它们存储在数据库中,然后再将其添加为项目中的外键。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文