unit testingDbContext
我研究了一些关于我可以用来testingDbContext的技术的信息。 我想添加一些内存数据的上下文,以便我的testing可以运行它。 我正在使用数据库优先方法。
我发现最有用的两篇文章是这个和这个 。 这种方法依赖于创buildMyContext和FakeContext将实现的IContext接口,允许模拟上下文。
然而,我试图避免使用存储库来抽象EF,正如一些人所指出的 那样 ,因为EF 4.1已经通过DbSet和DbContext实现了存储库和工作模式单元,而且我真的想保留EF实现的所有function团队不必像我在其他项目中那样使用通用的存储库自己维护它们(这是一种痛苦)。
使用IContext将使我走向相同的path(或不是吗?)。
我想过创build一个从主MyContextinheritance的FakeContext,从而利用它下面的DbContext来运行我的testing而不碰到数据库。 我找不到类似的实现,所以我希望有人能帮助我。
我做错了什么,或者这可能导致我一些我不期待的问题?
问自己一个问题:你要testing什么?
你提到了FakeContext
并嘲笑了上下文 – 为什么要同时使用? 这些只是不同的方式来做同样的事情 – 提供testing只执行的上下文。
还有一个更大的问题 – 伪造或模拟上下文或设置只有一个结果:你不再testing你的真实代码。
简单的例子:
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中有这个系统(被测系统 – 在unit testing的情况下,它是一个单元=通常是一个方法)。 在testing代码中,您提供了FakeContext
和FakeSet
,它将起作用 – 您将进行绿色testing。 现在在生产代码中,您将提供另一个派生的DbContext
和DbSet
,您将在运行时得到exception。
为什么? 因为通过使用FakeContext
您也更改了LINQ提供程序,而不是将LINQ to Entities运行到实体,因此调用无法转换为SQL的本地.NET方法以及LINQ to Entities中不可用的许多其他LINQfunction! 还有其他的问题,你可以find数据修改以及 – 参照完整性,级联删除等,这就是为什么我认为处理上下文/ LINQ to Entities的代码应该覆盖集成testing,并针对真正的数据库执行。
从EF 4.3起,您可以在创build上下文之前通过注入一个伪造的DefaultConnectionFactory
来unit testing您的代码 。
我正在开发一个开源库来解决这个问题。
有点戏弄
您不必添加任何样板代码,只需调用库的相应API即可,例如:
var context = Effort.ObjectContextFactory.CreateTransient<MyContext>();
起初这可能看起来很神奇,但创build的ObjectContext对象将与内存数据库进行通信,并且根本不会与原始的真实数据库进行通信。 术语“瞬态”是指这个数据库的生命周期,它只存在于创build的ObjectContext对象的存在期间。 同时创build的ObjectContext对象与专用数据库实例进行通信,数据不会相互共享。 这使得能够轻松地编写自动化testing。
该库提供了各种function来定制创build:跨实例共享数据,设置数据库的初始数据,在不同的数据层上创build假数据库…检查项目网站以获取更多信息。
entity framework4.1接近于能够在testing中被模拟,但需要一些额外的努力。 T4模板为您提供了一个包含DbSet属性的DbContext派生类。 我认为你需要模拟的两件事是这些属性返回的DbSet对象,以及在DbContext派生类中使用的方法和方法。 两者都可以通过修改T4模板来实现。
布伦特·麦肯德里克(Brent McKendrick)已经展示了这篇文章需要修改的types,但是不能修改T4模板。 粗略地说,这些是:
- 将DbContext派生类的DbSet属性转换为IDbSet属性。
- 添加一个为DbContext派生类生成接口的部分,该类包含IDbSet属性以及其他任何需要模拟的方法(如SaveChanges)。
- 在DbContext派生类中实现新接口。
对于还在寻找答案的人来说 – 我写了一个简单的库来方便地嘲讽DbContext一个非常简单的方法。 有关详细信息,请参阅我的其他答案以类似的问题: https : //stackoverflow.com/a/33716219/111438 。
PS – 我同意Ladislav Mrnka所说的,但是我认为在某些情况下,你需要嘲笑你的DbSet并运行unit testing是不可避免的。 虽然你需要记住,你不应该testing你的LINQ查询来validation它们是否返回正确的数据。 这就是集成testing更适用的地方。