如何使用NUnit有效地对使用ADO.NET和SQL Server的数据访问层进行单元测试?

6

所以,您在C#中有一个使用仓储模式的数据访问层(DAL),每个仓储都有一个接口。它是由ADO.NET、MS SQL Server和存储过程调用支持的。

这对于在单元测试中使用别处使用的存储库进行存根/模拟非常好,我喜欢它!

但是,我想为DAL本身添加一些单元测试。最好使用Rhino Mocks和NUnit。然而,如果可以明确地证明MoQ在这个问题上可以做到Rhino不能做到的事情,我也可以考虑切换。

在单元测试期间,我宁愿不让它与任何数据库通信,以使它们更加“纯净”,同时仍然有效地测试DAL本身。如果无法在DAL本身不与数据库交互的情况下有效地进行单元测试,那么替代方案将是内存替换或可移植基于文件的替换,它们与SQL Server的相同SqlConnection和SqlCommand调用兼容。

如果需要,可以重构DAL以使其更易于注入,前提是不会过于复杂化设计。已经使用Microsoft Unity作为DI来实现仓储模式。

1个回答

7
“单元测试”具体会测试什么内容呢?考虑到典型的数据访问层(DAL)代码很少超出将实际工作委托给第三方数据访问代码(ADO.NET、EntityFramework、NHibernate)的范畴。
我并没有太多使用ADO.NET的经验,但我想这个NHibernate示例足以说明问题:
public User GetUser(int id)
{
    using (var session = sessionFactory.OpenSession())
    {
        return session.Get<User>(id);
    }
}

当然,假设代码编写正确,所有依赖项(即sessionFactory)都将通过接口传递,因此很容易进行存根,因此编写单元测试是可行的(您只需要模拟大量内容使其发生)。
纯粹的方法是编写这样的单元测试,因为它是证明您的代码确实执行您所假定操作(调用Get)的另一个数据点。但是,请注意,这样的测试相当昂贵,因为您基本上必须存根提供程序所进行的整个数据库访问(模拟sessionFactory以返回模拟会话,然后检查所做的调用)。
这是一个难以做出的决定(是否在这种情况下遵循纯粹的方法),因为您仍然需要编写带有真实数据库的integration tests。而那些测试将涵盖与您编写的单元测试完全相同的领域,但在实际环境中工作(而不是在存根环境中)。
根据我的经验,对DAL进行单元测试通常不值得它提供的好处,特别是与其相对较高的成本(花费时间编写用于存根数据库环境的代码)和适当的集成测试套件的存在相比。
最后,我建议不要在任何类型的测试中使用内存数据库。原因如下:
  • 它很慢
  • 它有几个可能失效的点,可能很难控制(ORM配置、内存数据库设置、可能无效的测试数据)
  • 给你一种错误的集成测试感觉(而实际上并非如此;内存数据库可能会表现出与您的真实生产数据库完全不同的行为)
除非您能够将完全相同的数据库加载到内存中,否则请坚持使用适当的集成测试(即DAL测试优先),并进行孤立的单元测试备份(只有在您负担得起编写它们的情况下)。

抱歉如果我表达不清楚,这只涉及到ADO.NET、SQL Server和存储过程,没有ORM。单元测试将测试您的SQL是否有效以及您是否将结果从SQL转换为存储库接口返回的POCO。您需要确保DAL具有有效的SQL语句并正确传递参数,并且数据类型的转换按预期工作。 - Rodney S. Foley
1
@RodneyFoley:是的,编写这样的测试的成本仍然很高(存根任何ORM或ADO代码都很困难)。而且,您基本上会完成与集成测试相同的工作。请注意,这些测试非常脆弱,即使SQL中最轻微的更改(例如格式),即使不会对应用程序工作产生任何影响,也会破坏单元测试。我并不是说“不要那样做”;我是在警告这些测试将是昂贵的,并且已经有了廉价的替代方案,即集成测试(无论如何都应该进行)。 - k.m
我同意你在答案中的观点。我想我会编写单元测试来确保类型转换正确,然后将其余部分推送到我们的集成测试中并完成。对于快速的开发人员检查,我可能会编写一些单元测试,实际上是带有ignore标志的集成测试,以便在提交SQL语句更改之前根据需要运行。谢谢Jimmy! - Rodney S. Foley

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