我研究过一些关于我可以用来单元测试DbContext 的技术的信息。我想在上下文中添加一些内存数据,以便我的测试可以针对它运行。我正在使用Database-First方法。
我发现最有用的两篇文章是this和this。 该方法依赖于创建一个MyContext和FakeContext都将实现的IContext接口,允许模拟上下文。
但是,我试图避免使用存储库来抽象EF作为pointed by some人,因为EF 4.1已经通过DbSet和DbContext实现了存储库和工作单元模式,我真的希望保留EF团队实现的所有功能,而不必使用通用存储库来维护它们,就像我在其他项目中所做的那样(这有点痛苦)。
使用IContext会引导我走同一条路(或者不是吗?)。
我想过创建一个继承自主MyContext 的FakeContext,从而利用它下面的DbContext来运行我的测试,而不需要访问数据库。 我找不到类似的实现,所以我希望有人能帮助我。
我做错了什么,或者这会导致我遇到一些我没想到的问题?
答案 0 :(得分:35)
问自己一个问题:你要测试什么?
你提到FakeContext
并模仿上下文 - 为什么同时使用它们?这些只是做同样的不同方式 - 提供仅测试上下文的实现。
还有一个更大的问题 - 伪造或模拟上下文或集只有一个结果:你不再测试你的真实代码了。
简单示例:
public interface IContext : IDisposable
{
IDbSet<MyEntity> MyEntities { get; }
}
public class MyEntity
{
public int Id { get; set; }
public string Path { get; set; }
}
public class MyService
{
private bool MyVerySpecialNetMethod(e)
{
return File.Exists(e.Path);
}
public IEnumerable<MyEntity> GetMyEntities()
{
using (IContext context = CreateContext())
{
return context.MyEntities
.Where(e => MyVerySpecialNetMethod(e))
.Select(e)
.ToList();
}
}
}
现在假设你在SUT中有这个(被测系统 - 在单元测试的情况下它是一个单位=通常是一个方法)。在测试代码中,您提供FakeContext
和FakeSet
并且它将起作用 - 您将进行绿色测试。现在,在生产代码中,您将提供另一个派生的DbContext
和DbSet
,并且您将在运行时获得异常。
为什么呢?因为通过使用FakeContext
,您还更改了LINQ提供程序而不是LINQ to Entities,而是运行LINQ to Objects,因此调用无法转换为SQL的本地.NET方法以及许多其他不可用的LINQ功能在LINQ to Entities中!您还可以通过数据修改找到其他问题 - 参照完整性,级联删除等。这就是为什么我认为处理context / LINQ to Entities的代码应该用集成测试覆盖并针对真实数据库执行的原因。
答案 1 :(得分:13)
我正在开发一个开源库来解决这个问题。
一个小预告:
您不必添加任何样板代码,只需调用库的相应API,例如:
var context = Effort.ObjectContextFactory.CreateTransient<MyContext>();
首先,这看起来很神奇,但创建的ObjectContext对象将与in-memory database进行通信,并且根本不会与原始真实数据库通信。术语“瞬态”是指该数据库的生命周期,它仅存在于创建的ObjectContext对象期间。并发创建的ObjectContext对象与专用数据库实例进行通信,数据不会与它们共享。这样可以轻松编写自动化测试。
该库提供了各种功能来自定义创建:跨实例共享数据,设置数据库的初始数据,在不同的数据层上创建假数据库...查看project site以获取更多信息。
答案 2 :(得分:12)
从EF 4.3开始,您可以unit test your code在创建上下文之前注入假DefaultConnectionFactory
。
答案 3 :(得分:5)
实体框架4.1几乎可以在测试中进行模拟,但需要额外的努力。 T4模板为您提供包含DbSet属性的DbContext派生类。我认为你需要模拟的两件事是这些属性返回的DbSet对象以及你在DbContext派生类上使用的属性和方法。两者都可以通过修改T4模板来实现。
Brent McKendrick已经展示了需要在this post中进行的修改类型,而不是可以实现此目的的T4模板修改。粗略地说,这些是:
答案 4 :(得分:1)
对于仍在寻找答案的人 - 我写了一个简单的库,以便于以非常简单的方式模拟DbContext。有关详细信息,请参阅我对类似问题的其他答案:https://stackoverflow.com/a/33716219/111438。
PS - 我同意Ladislav Mrnka的观点,但我认为在某些情况下,你需要模拟你的DbSet并对其进行单元测试是不可避免的。虽然您需要记住,您不应该测试LINQ查询以验证它们是否返回正确的数据。这就是集成测试更适用的地方。