Entity Framework 是否有内存提供程序?

40

我正在对针对ADO .NET Entity Framework编写的代码进行单元测试。我想使用行填充内存数据库,并确保我的代码正确地检索它们。

我可以使用Rhino Mocks模拟Entity Framework,但这还不够。我会告诉查询返回哪些实体给我。这既不能测试where子句也不能测试.Include()语句。我想确保我的where子句仅与我想要匹配的行匹配,没有其他行。我想确保我请求的实体是我需要的,而没有我不需要的。

例如:

class CustomerService
{
    ObjectQuery<Customer> _customerSource;
    public CustomerService(ObjectQuery<Customer> customerSource)
    {
        _customerSource = customerSource;
    }
    public Customer GetCustomerById(int customerId)
    {
        var customers = from c in _customerSource.Include("Order")
            where c.CustomerID == customerId
            select c;
        return customers.FirstOrDefault();
    }
}

如果我模拟ObjectQuery返回一个已知的客户,填充了订单,那么我如何知道CustomerService有正确的条件和Include?我宁愿插入一些客户行和一些订单行,然后断言选择了正确的客户并且订单已经被填充。


1
就像你所做的那样,我使用接口来遵循存储库模式和工作单元模式。然后,我有两个命名空间-> EF和Fake。对于我的虚拟存储库,我只是使用IList<POCO>来存储我的东西,并利用Linq to Objects来提取数据。效果非常好 :) - Pure.Krome
4
EntityFramework 7现在有InMemory提供程序了。截至评论时仍处于测试阶段,但如果您订阅夜间nuget,就可以获得它。 - Piotr Kula
8个回答

16

4
虽然这是真的,但EF7仍然处于预发布阶段,微软已经声明EF7并不只是EF6的下一个版本,这也是为什么他们将其重新命名为EF Core 1.0的原因。 - Tim Long
我们的团队决定在复杂类型和TPT实现之前不去碰EF7。EF7还太原始了。 - Shimmy Weitzhandler

13

9

在这里更好的方法可能是使用仓库模式来封装 EF 代码。在测试您的服务时,您可以使用模拟或伪造数据。在测试您的存储库时,您将希望访问真实的数据库以确保您得到预期的结果。


如果我只是模拟存储库以返回给定的客户,这并不能测试规范。我想在单元测试中包含规范,而不是等到我使用真正的数据库时再进行测试。 - Michael L Perry
1
哈哈.. 那么如何使用存储库模式测试LINQ2Entities查询呢?这是不可能的,因为这些查询将生成SQL查询语句,我们还需要在数据库中对它们进行测试。 - zihotki
1
我接受这个答案,因为它是最接近我实际所做的事情。我创建了符合存储库模式的接口,然后实现了EF适配器和内存测试工具。它没有测试数据库,但确实测试了我的规范。 - Michael L Perry
可以使用Moq和InMemoryDbSet。这对于内部连接的LINQ查询很好,但对于外部连接则不起作用。 - Kremena Lalova
3
在DbContext之上创建一个仓储库是一种泄漏的抽象,它会带来负面影响而非好处。 - SuperJMN

7
目前EF还没有提供内存提供程序,但是如果您查看Highway.Data,它有一个基础抽象接口和InMemoryDataContext。请参考Testing Data Access and EF with Highway.Data以帮助测试数据访问和EF。

8
这个回答已经不正确了。EF7现在支持InMemory... 会在不太遥远的未来发布。 - Piotr Kula
3
截至本文撰写时,它仍处于Beta版本。在NuGet上查找EntityFramework.InMemory。 - HiredMind
尽管EF7将包括InMemory提供程序...但我理解InMemory提供程序高度抽象化,因为它模仿所有提供程序的常见特征。对于需要验证某些特定于提供程序的概念的单元测试,您需要使用类似EntityFramework.SqlLite提供程序的东西。例如,我无法使用InMemory提供程序验证唯一约束或必填字段约束。您也无法使用它验证关系约束。更多详细信息请参见:https://github.com/aspnet/EntityFramework/issues/2166 - Paul
1
值得一提的是,我们使用实时开发数据库(因为我们活跃地将其用于沙盒),并且只需在事务范围内包装测试类即可。https://msdn.microsoft.com/zh-cn/library/aa833153(v=vs.100).aspx - kamranicus

6

是的,至少有一个这样的提供商-SQLite。 我用过它,它很好用。 另外,您还可以尝试SQL Server Compact。 它是一个嵌入式数据库,并且也有EF提供程序。
编辑:
SQLite支持内存数据库(link1)。 您只需要指定连接字符串,如:"Data Source=:memory:;Version=3;New=True;"。 如果您需要示例,可以查看SharpArchitecture


我可以使用嵌入式(进程内)数据库,但我更喜欢完全使用内存。我想在代码中定义我的表并填充一些行,从不将任何内容写入磁盘。然后,我可以让单元测试验证我的CustomerService正确地查询了这些数据。 - Michael L Perry
我更喜欢在内存中运行实际的数据库,因为测试最重要的是数据库关系和约束,而在代码中很难得到支持。让我困扰的是这是应用逻辑,应该在模型中表示,而不是在数据库中。 - terjetyl
完美的情况是有一些类似于数据库表、它们之间关系和约束的 .net 集合,这将允许您在内存中创建整个数据库,并允许您创建 ddl 以便在测试完成后导出到真正的数据库。 - terjetyl
使用NHibernate进行对象关系映射(ORM)。你的实体将会呈现数据库的结构。当你使用领域驱动设计(DDD)时,这个结构可以轻松地导出到许多不同的数据库中。同时,它也可以很方便地通过内存中的SQLite数据库进行测试。可以参考SharpArchitecture进行实例学习。 - zihotki
请注意,如果模型包含时间数据类型,则目前似乎Entity Framework中的SQLite不支持:https://dev59.com/eI7da4cB1Zd3GeqP-kDh(我尝试使用SQLite nuget包v1.0.105.1)。 - danio

2

我不熟悉Entity Framework和ObjectQuery类,但是如果Include方法是虚拟的,您可以像这样模拟它:

// Arrange
var customerSourceStub = MockRepository.GenerateStub<ObjectQuery<Customer>>();
var customers = new Customer[] 
{
    // Populate your customers as if they were coming from DB
};
customerSourceStub
    .Stub(x => x.Include("Order"))
    .Return(customers);
var sut = new CustomerService(customerSourceStub);

// Act
var actual = sut.GetCustomerById(5);

// Assert
Assert.IsNotNull(actual);
Assert.AreEqual(5, actual.Id);

GetCustomerById方法有两个特点:where子句和include。如果我模拟客户源返回已知的客户,那么我将无法测试它们中的任何一个。 - Michael L Perry
如果你模拟Include方法,你不是返回一个已知的客户,而是返回一个已知客户列表,这样你就可以测试where子句,看它是否通过id找到了客户。就Include方法而言,你还要验证你是否使用正确的参数调用它:Order。 - Darin Dimitrov

1
你可以尝试使用SQL Server Compact,但它有一些相当严格的限制:
  • 当与Entity Framework一起使用时,SQL Server Compact不支持在分页查询中使用SKIP表达式。
  • 当与Entity Framework一起使用时,SQL Server Compact不支持具有服务器生成的键或值的实体。
  • 没有外连接、排序方式、浮点数模运算、聚合函数。

0

EF Core中,有两个主要选项可以实现这一点:

  1. SQLite内存模式允许您编写有效的测试,针对行为类似于关系型数据库的提供程序。
  2. InMemory提供程序是一个轻量级的提供程序,具有最小的依赖关系,但并不总是像关系型数据库那样运行。

我正在使用SQLite,并且它支持我需要在Azure SQL生产数据库中执行的所有查询。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接