【问题标题】:Replicating LINQ to Entities behavior in unit tests that use LINQ to Object在使用 LINQ to Object 的单元测试中复制 LINQ to Entities 行为
【发布时间】:2011-09-19 19:13:21
【问题描述】:
在我的解决方案中,我有不依赖于数据库的单元测试,而是模拟和集成测试,它们具有像数据库这样的外部依赖项。
当我使用 mock 进行单元测试时,会使用 LINQ to Object。当集成测试或实际程序运行时,使用 LINQ to Entities,它比 LINQ to Object 更严格。
有没有办法在我的单元测试中复制 LINQ To Object 更严格的行为?
【问题讨论】:
标签:
linq
unit-testing
entity-framework
【解决方案1】:
您可以使用repository模式来定义IRepository接口,在您的实际代码中,您可以使用Entity Framework来实现它,在您的单元测试中,您可以使用mock framework返回对象进行测试。
【解决方案2】:
如果您希望与数据库对话的层的单元测试具有真正的价值和所有行为,就像在生产代码中一样,您需要与数据库对话。它可能被认为违反了在单元测试中没有依赖关系的规则,但是除了让它与 real 数据库对话之外,没有其他方法可以让 LINQ to Entities 完成其工作。它可能(也可能应该)是一个内存数据库——例如参见how is Ayende doing it。
【解决方案3】:
一旦您的单元测试逻辑与 EF 提供的 IQueryable 一起工作,您就无法通过模拟 EF 来使用单元测试对其进行测试。这总是会导致从 linq-to-entities 切换到 linq-to-objects。如果有一些简化的东西,在单元测试中处理这个问题的正确方法是写一个假的而不是模拟的。 Fake 会模拟依赖的行为。在这种情况下,编写一个假的意味着编写 EF 提供程序处理内存中的集合,其方式与真实提供程序处理数据库的方式完全相同。编写这样的提供者可能是项目本身。
因此,一旦您的逻辑包含 linq-to-entities 查询,您应该始终使用集成测试对其进行测试或重构代码,以便查询本身在单独的方法中(通过集成测试进行测试),并且以前的逻辑现在依赖于包含该方法的类而不是 EF 本身 - 这会导致存储库模式,其中 IQueryalbe 未公开,但存储库公开了在某个实体上运行的每个所需查询的方法。我个人不喜欢这种存储库。 Here is recent discussion 关于不同的存储库实现。
如果您决定使用集成测试将数据库更改为内存中,则可以使用 EFv4.1 和代码首先将连接字符串更改为 SQL Compact 4 并且它将起作用(除非您使用一些特殊的直接 SQL在映射中调用或要求一些特殊的 SQL 类型)。如果 EF 带有 EDMX 文件,它将不起作用,因为 EDMX 文件与确切的数据库版本紧密耦合。仅用于单元测试的特殊 EDMX 不是一种选择,因为您将再次测试不同的代码。
Here is set of related questions 讨论单元测试 EF 代码和存储库的挑战。