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代​​码中,您提供了FakeContextFakeSet ,它将起作用 – 您将进行绿色testing。 现在在生产代码中,您将提供另一个派生的DbContextDbSet ,您将在运行时得到exception。

为什么? 因为通过使用FakeContext您也更改了LINQ提供程序,而不是将LINQ to Entities运行到实体,因此调用无法转换为SQL的本地.NET方法以及LINQ to Entities中不可用的许多其他LINQfunction! 还有其他的问题,你可以find数据修改以及 – 参照完整性,级联删除等,这就是为什么我认为处理上下文/ LINQ to Entities的代码应该覆盖集成testing,并针对真正的数据库执行。

从EF 4.3起,您可以在创build上下文之前通过注入一个伪造的DefaultConnectionFactory来unit testing您的代码 。

我正在开发一个开源库来解决这个问题。

http://effort.codeplex.com

有点戏弄

您不必添加任何样板代码,只需调用库的相应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模板。 粗略地说,这些是:

  1. 将DbContext派生类的DbSet属性转换为IDbSet属性。
  2. 添加一个为DbContext派生类生成接口的部分,该类包含IDbSet属性以及其他任何需要模拟的方法(如SaveChanges)。
  3. 在DbContext派生类中实现新接口。

对于还在寻找答案的人来说 – 我写了一个简单的库来方便地嘲讽DbContext一个非常简单的方法。 有关详细信息,请参阅我的其他答案以类似的问题: https : //stackoverflow.com/a/33716219/111438 。

PS – 我同意Ladislav Mrnka所说的,但是我认为在某些情况下,你需要嘲笑你的DbSet并运行unit testing是不可避免的。 虽然你需要记住,你不应该testing你的LINQ查询来validation它们是否返回正确的数据。 这就是集成testing更适用的地方。