使用实体框架进行单元测试


Answers:


50

对于Enity Framework 4,这看起来很有希望:可测试性和Entity Framework 4.0


现在要阅读。许多EF测试问题和解答不是最新的,因此EF 4.0链接为+1。
StuperUser

1
我们的商店开始实施此处看到的技术,到目前为止,似乎已经是答案。
Dan Bailiff,

3
仅供参考,他从不将上下文放在UnitOfWork类中。在此示例中,asp.net/entity-framework/tutorials/…,UnitOfWork类实现IDisposable。结合这两种方法,并用IDisposible装饰Hans答案中的IUnitOfWork接口,并实现Dispose模式,例如msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.71).aspx
James McLachlan,


6

一种便宜的方法是设置一个与真实数据库具有相同结构的数据库文件,并在单元测试配置中设置连接字符串以指向该文件。数据库并不需要拥有真正的所有表。只是单元测试所需要的。

缺点是您需要管理数据库的状态,以使单元测试在运行期间和运行之间不会互相影响。

我知道当实际和单元测试DB都使用SQL Express时,这种方法有效,但是我不知道在SqlExpress DB中存根以获得完整的SQL DB。

我意识到这是技术上的集成测试,但是它比重构代码或学习模拟框架要便宜。

实际连接字符串示例:

<add name="DrinksEntities" 
     connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient
     ;provider connection string=&quot;Data Source=localhost\sqlexpress;Initial Catalog=Drinks2;Integrated Security=True;MultipleActiveResultSets=True;Application Name=EntityFramework&quot;" 
     providerName="System.Data.EntityClient" />

单元测试连接字符串示例:

<add name="DrinksEntities" 
     connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient
     ;provider connection string=&quot;Data Source=.\SQLEXPRESS;attachdbfilename=|DataDirectory|\Inventory.mdf;Integrated Security=True;user instance=True;MultipleActiveResultSets=True;Application Name=EntityFramework&quot;" 
     providerName="System.Data.EntityClient" />

4
到目前为止,这对我来说似乎是最好的解决方案。如果要加速,请在RAM中运行整个数据库。这些是端到端测试。模拟解决方案根本不是解决方案。测试数据库的重点不仅在于测试持久性存储,还在于测试查询。查询测试仅在实际执行时才有意义-在这里进行模拟只是浪费时间-您需要伪造的表数据。我尝试为Linq2Sql创建一整套伪造的存储库,但这是浪费时间,因为无法测试使用左连接的查询-我担心EF也会出现相同的问题。
user1040323'8

2
设置数据非常容易-请阅读Microsoft关于单元测试存储过程的建议。同样的原则适用-您开始事务,截断表并插入所需的测试数据;然后运行测试,最后回滚事务。数据库最终以其开始的状态运行,您可以管理每个测试的测试数据。
gbjbaanb 2013年

3
这似乎是完全测试我的应用程序的唯一方法。模拟IRepository将使您能够测试使用数据访问代码的代码,而不是使用实际数据访问代码的代码,这是我90%的错误所在。
布里顿2014年

4

您将要使用模拟框架来检索模拟值,而不是访问实际数据。以下是一些模拟框架的列表,以及指向一些截屏视频的链接,以帮助您入门:

以下是一些有关如何入门的截屏视频:


8
使用EF很难做到这一点。您的答案对于接口或可扩展POCO是正确的。




2

我同意,您追求的是一个模拟框架。您创建未从数据源中检索到的“模拟”对象,然后测试该对象中的数据。我个人一直在与Moq合作,我喜欢它-还有Rhinomocks,还有其他产品。


2

在对此感到无奈之后,我终于有了至少对部分问题感到满意的解决方案。

首先使用存储库接口,例如:

public interface IRepository
{
    IQueryable<T> GetObjectSet<T>();
}

我们可以使用它返回内存中的集合或真实的数据库支持的集合。接下来,将查询封装到带有如下所示接口的查询对象中。

public interface IQuery<T>
{
    IQueryable<T> DoQuery(IQueryable<T> collection);
}

现在将您的单元测试分为2组。第一组将测试您的查询是否有效。这样做是这样的:

[TestMethod]
public void TestQueryFoo()
{
     using(var repo = new SqlRepository("bogus connection string"))
     {
         var query = new FooQuery(); // implements IQuery<Foo>
         var result = query.DoQuery(repo.GetObjectSet<Foo>());  // as long as we don't enumerate the IQueryable EF won't notice that the connection string is bogus
         var sqlString = ((System.Data.Objects.ObjectQuery)query).ToTraceString(); // This will throw if the query can't be compiled to SQL
     }
}

然后,第二组单元测试可以自由地测试您的业务逻辑,而不必担心SQL编译步骤(到目前为止,这是我们遇到的最大麻烦)。

无论如何,它都不是完美的,显然不会运行触发器,可能会违反数据库实现的约束,并且上下文和数据库不同步的某些问题会出现。因此,尽管仍然需要端到端集成测试,但有可能发现在简单的单元测试中,IMO在运行时最常见的问题。


您能否使用这种方法提供一些示例代码?我也想对使用Entity Framework的Web api 2项目进行单元测试
Jibin Mathew


0

这是工作单元模式+内存数据库+ t4代码生成的汇总,以自动生成伪造的EF dbContext。

http://mockingcompetence.wordpress.com/2013/05/20/fakingefdatacontext/

目前完全复制真实的EF数据库连接存在一些问题(对EF查询的无效linq且无FK强制执行)。

但是,具有内存上下文来快速运行单元测试对于能够进行TDD或任何其他类型的以单元测试为中心的方法几乎至关重要。

当我发现更多问题时,将在上面的链接中发布更新。


0

您可以使用内存中的数据库来测试您的实体框架模型。看这里了解更多详情


-1

如何使用模拟框架?在我看来,模拟框架可以帮助您从数据库中隔离业务逻辑。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.