【问题标题】:How to unit test Entity Framework?如何对实体框架进行单元测试?
【发布时间】:2012-08-25 04:35:42
【问题描述】:

我正在尝试首先使用 EntityFramework 代码测试我的代码。为了使其可测试并允许隔离测试,我创建了我的DbContext 实现的接口。我没有测试 DbContext 类 - 我将假设 EF 代码按预期工作。

现在,考虑以下方法:

public IEnumerable<User> GetOddId()
{
    return context_.Users.Where((u, i) => i % 2 == 1).AsEnumerable();
}

此方法将通过我的模拟 FakeDbSet(因为它会使用内存中的 LINQ 提供程序)而通过,而在使用 EF/LINQ to SQL 驱动程序时它会失败并出现异常。

您会保持原样并希望人们知道的足够多,不要写这样的查询吗?您会放弃隔离测试并在实际数据库上进行测试吗?

带有 DataMigrations(可能带有适当的种子)的 LocalDb 是否有助于在实际数据库上进行测试?

请证明答案。

TLDR:如何测试 EntityFramework 代码,考虑内存 LINQ 和 SQL LINQ 之间的差异?

很久以后的编辑:我已经找到了一个非常好的框架,可以完全满足我的需要。我写了一篇关于unit testing with Effort 的博文。另请注意,即将推出的 EF6 可能不需要所有这些,它承诺提供一些单元测试功能。

【问题讨论】:

    标签: c# linq unit-testing ef-code-first


    【解决方案1】:

    为此,我们使用 SQLite 的内存数据库。它们的创建、查询和拆除速度极快,对整体测试速度几乎没有任何影响。一旦您为自己设置了一个测试框架来创建数据库并注入数据,就可以快速编写测试。

    当然,SQLite 是一个比大多数数据库简单得多的数据库,因此复杂的查询可能无法转换为它的 SQL 版本,但对于测试 90% 的情况,它运行良好。

    这些测试是否构成集成测试?我不这么认为。他们仍然只测试 your 代码的一个单元,即生成 LINQ 查询的位。您正在测试两件事:1) 查询返回正确的数据(但您可以使用您所说的内存中集合来检查),以及 2) 实体框架可以将查询转换为有效的 SQL。测试后者的唯一真正方法是在真正的实体框架上触发查询,但使用 stubbed 数据库。

    虽然您可能会争辩说,真正的单元测试应该只测试代码的输出(即解析和检查已生成的表达式树),并且更难编写,但这并不能证明任何事情。例如,如果您修改代码以生成内部连接而不是子查询,您是否希望测试中断?只有当它返回不同的结果时,我才会想到。

    【讨论】:

    • 是的,这也是我过去做过的事情。不过,就像您说的那样,“精简”版本的数据库中有一些功能没有像它们功能齐全的兄弟那样实现。这真的取决于你的项目(就像大多数事情一样;D)
    • 这可能是迄今为止最好的答案,我正在考虑一些非常相似的东西,即使用 LocalDb。但是,使用这种集成测试并且无法单独测试确实感觉不对。我还必须事先进行一些性能测试。
    • @VladCiobanu 有趣的是,您将这些视为集成测试。这可能有点主观,但我在答案中添加了一些内容,所以让我知道你的想法。
    • @TimRogers 好点,我今天实际上在争论在数据层中访问数据库是否真的意味着它是一个集成测试。主要问题仍然存在,如果我决定在我的数据层之外公开 IQueryable 接口(并且我发现这样做有很多好处),那么我将永远不必真正模拟数据层,以避免生成查询不转换为有效的 SQL。对这个问题有什么想法吗?
    • @VladCiobanu 是的,这是一个很好的观点,但问题是 LINQ to EF 是一个泄漏的抽象——您可以根据底层技术创建无效的查询。公开IQueryable 可能有其好处,但您也在将这种泄漏传播到堆栈中。它允许您的顶级代码调用错误或效率极低的数据库——这是数据层应该处理的问题。这是一个权衡。问问自己,如果你公开了IQueryable,你的数据层到底在做什么?
    【解决方案2】:

    在我工作的地方,我们有一个开发/测试版/生产 SQL 服务器。有时我们会在对实际数据库执行测试之前针对 beta 和种子测试数据创建测试(例如,在测试特定选择之前插入等)。人们会区分单元测试和集成测试,但它至少可以让我们测试我们的逻辑,这比仅仅交叉手指并希望在数据访问层做到最好。

    易于测试但在某些重要情况下实际上并不代表实时系统的内存提供程序有什么好处?

    编辑:我们不使用 EF,顺便说一句...

    【讨论】:

    • 正如我所说,我也在考虑使用 LocalDb 来完成您所描述的基本相同的事情。我唯一担心的是执行速度可能会极大地影响测试,从而使我们减少运行它们的频率。你有这个问题吗?开发人员不经常运行测试是因为在实际数据库上运行速度很慢?
    • 我们没有遇到速度问题。我也听到了同样的情绪,但这对我们来说不是问题。我们将 TeamCity 用于 CI,它与 SVN 挂钩,可在签入后启动构建。我确信它比内存测试要慢,但我对它更有信心。在一个给定的项目中,我们可能有几十个测试。但是,我们在服务器上有(字面上)几十个解决方案。
    【解决方案3】:

    您可能想要使用 Telerik 的 JustMock 之类的模拟框架(或从许多其他框架中选择)。

    这将使您对测试代码中发生的事情有很大的控制权。 (简介here。)

    您可以“模拟”查询并返回预定义的对象集合,而不是实现对真实数据库的查询。

    例如,您可以创建多个调用 GetOddId() 方法的单元测试,并定义涵盖您需要的所有测试用例的不同返回集合(空列表、正确内容、错误内容、抛出异常等) , ...)。

    还有一个免费的“精简版”版本here 或通过 NuGet。

    【讨论】:

    • 如果开发人员意识到在 LINQ to SQL 中调用该特定版本的 Select 方法将不起作用,那么这将起作用。但是,问题是她可能不会,并且在测试时会出现误报。我没有尝试过 JustMock,但是浏览了这些功能,我怀疑它甚至能够警告此类可能的问题(只是一个模拟框架)。
    猜你喜欢
    • 2012-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2013-10-17
    相关资源
    最近更新 更多