C# 如何防止在单元测试中自动包含导航属性

C# 如何防止在单元测试中自动包含导航属性,c#,sqlite,unit-testing,entity-framework-core,ef-core-2.2,C#,Sqlite,Unit Testing,Entity Framework Core,Ef Core 2.2,我目前正在开发一个应用商店风格的API,它有以下实体(加上许多其他实体,但与问题无关): 应用程序(与AppRevision的1对多关系-包含IEnumerable属性) 通知 装置 我遇到了一个奇怪的问题,即EF在单元测试中的行为与实际运行API时的行为不同,在单元测试时导航属性会自动包括在内 从我的命令处理程序中获取以下代码片段: App app = await this.context.Apps .Include(a => a.Installations) .Fi

我目前正在开发一个应用商店风格的API,它有以下实体(加上许多其他实体,但与问题无关):

  • 应用程序(与AppRevision的1对多关系-包含IEnumerable属性)
  • 通知
  • 装置
我遇到了一个奇怪的问题,即EF在单元测试中的行为与实际运行API时的行为不同,在单元测试时导航属性会自动包括在内

从我的命令处理程序中获取以下代码片段:

App app = await this.context.Apps
    .Include(a => a.Installations)
    .FirstOrDefaultAsync(a => a.Id == command.AppId);

if (app != null) {
    // Code omitted for brevity
}
运行API时,如果我在运行此代码后检查
app
,则应用实体上的AppRevisions集合将为空,正如您所料,因为我没有明确告诉EF要
。Include(a=>a.AppRevisions)
-API随后在尝试处理需要此数据的代码时会引发异常

现在看一下相同处理程序的以下单元测试:

[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
    Guid testGuid = Guid.NewGuid();

    CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc@abc.com", null);

    using (TestContext context = new TestContextFactory().CreateTestContext())
    {
        context.Apps.Add(new App() { Id = testGuid });
        context.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
        await context.SaveChangesAsync();

        CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(context);

        CommandResult result = await handler.Handle(command, new CancellationToken());

        Assert.True(result);
        Assert.Single(context.Installations);
    }
}
如果我单步执行此测试,当我到达处理程序并检查
app
变量时,AppRevisions集合已自动填充。因此,测试通过,因为需要填充AppRevisions集合的代码可以执行

我们的期望是,这个测试实际上应该失败,因为我没有告诉EF在查询中包含这些实体

我正在使用Sqlite内存数据库为我的单元测试和运行.NETCore2.2创建数据库上下文

我最初认为这与changetracker有关。虽然禁用此选项确实解决了上面报告的直接问题,但它会产生大量其他问题,因此不是可行的解决方案(而且可能不是正确的解决方案)


感谢收到的任何建议

对于将来遇到此帖子的任何人,解决方案是根据对原始问题的评论,使用单独的上下文来播种测试数据并在稍后的测试中获取数据:

[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
    Guid testGuid = Guid.NewGuid();

    CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc@abc.com", null);

    using (TestContextFactory contextFactory = new TestContextFactory())
    {
        using (TestContext seedContext = contextFactory.CreateTestContext())
        {
            seedContext.Apps.Add(new App() { Id = testGuid });
            seedContext.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
            await seedContext.SaveChangesAsync();
        }

        using (TestContext getContext = contextFactory.CreateTestContext())
        {
            CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(getContext);

            CommandResult result = await handler.Handle(command, new CancellationToken());

            Assert.True(result);
            Assert.Single(getContext.Installations);
        }
    }
}

您正在重用相同的上下文,因此可能存在appevision,因为它们是缓存的。尝试在SaveChangesSyncSee之后创建另一个上下文。不幸的是,他们删除了我所指的文档的一部分。但原则仍然存在——当查询启用跟踪的实体时,基本上可以控制加载到导航属性中的内容。如果你真的想要完全控制,那么我恐怕你应该切换到DTOs/ViewModels和projection。和往常一样,在单元测试中,确保尽可能接近真实的应用程序代码。API方法不接收“受污染”的上下文。因此,如前所述,为模拟API调用的部分使用新上下文。您可能必须检查此模式的其他测试。@Alberto是的,可能是这样,但当我使用内存数据库时,我无法创建其他上下文(或者我可以吗?)@pr.lwd您创建一个,这样您就可以创建两个、三个、四个。。。。