C# 如何防止在单元测试中自动包含导航属性
我目前正在开发一个应用商店风格的API,它有以下实体(加上许多其他实体,但与问题无关):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
- 应用程序(与AppRevision的1对多关系-包含IEnumerable属性)
- 通知
- 装置
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您创建一个,这样您就可以创建两个、三个、四个。。。。