我的存储库层中有这样的方法:
public IEnumerable<User> GetActiveUsers()
{
return dbContext.Users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.ToList();
}
我应该通过模拟DbContext
来对此方法进行单元测试,还是应该使用实际数据库对其进行测试?
我知道当我使用实际数据库时,它不再是"单元"测试,但我看不到模拟DbContext
来测试我的存储库方法的价值,这些方法逻辑很薄,通常只是直接调用 EF 的方法。
如果我必须使用实际的数据库,是否有任何标准策略来填充数据库中的测试数据,以便测试独立运行并且不会更改数据库中的任何状态?
不是你想听到的,但你不想嘲笑DbContext
.我知道它一直在做,在 EF6 中,它甚至比以前更容易。还有更多的接口和虚拟方法可用于实现模拟对象。从技术上讲,这并不难。
重要的是行为。
即使在您的小示例中,也可能存在一个问题。模拟DbSet
将执行区分大小写的排序。连接的DbSet
将从数据库接收排序的数据,并且许多数据库排序规则恰好不区分大小写。因此,单元测试可能会产生与集成测试不同的结果,即使在这种看似微不足道的情况下也是如此。
内存中和连接的 LINQ 之间的差异是压倒性的。就个人而言,我只对涉及任何 LINQ to 实体查询的任何内容进行集成测试。创建模拟对象图太容易了,如果 EF 会生成它,该图看起来会有所不同。在我的服务方法中,我有时会编写相当复杂的查询,可能涉及Includes
,可能故意省略null
守卫,知道语句被翻译成 SQL,可能涉及一些延迟加载或依赖关系修复。我必须了解实体状态、上下文生命周期、保存更改时启动的验证、并发性......我只是不相信绿色测试,当它都被嘲笑时。
当然,还剩下足够的业务逻辑来测试纯单元测试。一旦您可以假设正确的对象可用(因为您在集成测试中单独测试),您就可以模拟它们并在内存中对它们的行为进行单元测试。
另一方面:通过设置数据库并针对该数据库进行测试,您会获得哪些额外的信息?
我只是嘲笑DbContext
因为它最容易维护,并且因为本质上对实体框架对数据库的调用进行单元测试没有任何价值。
要测试的是 LINQ 查询是否从数据源返回数据。但是,这些 LINQ 查询由 EF 转换为 SQL 查询是你不会打扰的。
如果您真的想测试外部依赖项,您可以将其视为额外的集成测试,但 EF 本身已经非常可靠,所以我怀疑这是否会以任何方式有用。
如果IEnumerable GetActiveUsers被实现(它应该)作为一个接口,并且你发布的代码是一个实现接口的具体类。然后,您通常会使用 mock 框架来模拟您实现的接口并返回您设置的结果集。
正如 Jeroen 所提到的,您通常不需要对实体框架做什么进行单元测试。单元测试只是测试逻辑,而不是测试其他库(此处为 EF)逻辑。
在为 DBContext 编写模拟和执行 LINQ 查询时要记住的一点是,它们作为 LINQ to OBJECT,而不是 LINQ to Entities...(EF6)...
我个人会把你的时间花在测试行为上。 Julie Lerman 有一个系列 @ pluralsight 和 MSDN Mag 在线介绍了如何测试 dbContext(如果必须的话)。