单元测试数据库
去年夏天,我正在开发一个基本的ASP.NET/SQL Server CRUD应用程序,而单元测试是其中的要求之一。当我尝试对数据库进行测试时遇到了一些麻烦。据我了解,单元测试应该是:
- 无状态的
- 彼此独立
- 具有相同结果的可重复性,即没有持久的更改
在开发数据库时,这些要求似乎彼此矛盾。例如,如果不能确保没有要插入的行,就无法测试Insert(),因此我需要先调用Delete()。但是,如果他们还不在那里怎么办?然后,我需要先调用Exists()函数。
我最终的解决方案涉及非常大的设置功能(糟糕!)和一个空的测试用例,该用例将首先运行并指示安装程序没有问题。这在维持测试无状态的同时牺牲了测试的独立性。
我发现的另一个解决方案是将函数调用包装在一个可以轻松回滚的事务中,例如Roy Osherove的XtUnit。这项工作,但是涉及另一个库,另一个依赖项,对于解决当前的问题似乎有点过于繁重。
那么,SO社区在面对这种情况时做了什么?
tgmdbm说:
You typically use your favourite automated unit testing framework to perform integration tests, which is why some people get confused, but they don't follow the same rules. You are allowed to involve the concrete implementation of many of your classes (because they've been unit tested). You are testing how your concrete classes interact with each other and with the database.
因此,如果我正确阅读了这篇文章,实际上就没有办法有效地对数据访问层进行单元测试。或者,数据访问层的"单元测试"是否涉及测试(例如,由类生成的SQL /命令),而与与数据库的实际交互无关?
解决方案
回答
除了断言表存在,包含预期的列并具有适当的约束之外,没有真正的方法可以对数据库进行单元测试。但这通常不值得做。
我们通常不会对数据库进行单元测试。我们通常将数据库包含在集成测试中。
通常,我们会使用自己喜欢的自动化单元测试框架来执行集成测试,这就是为什么有些人会感到困惑,但他们却没有遵循相同的规则的原因。我们可以参与许多类的具体实现(因为它们已经过单元测试)。我们正在测试具体类之间以及与数据库的交互方式。
回答
数据库单元
我们可以使用此工具在给定的时间导出数据库的状态,然后在进行单元测试时,可以在测试开始时自动将其回滚到以前的状态。我在工作的地方经常使用它。
回答
单元测试中对外部依赖关系的通常解决方案是使用模拟对象,也就是说,模拟我们要测试的真实对象的库。这并不总是那么简单,有时还需要一些独创性,但是如果我们不想"自己动手",那么有几个不错的(免费软件).Net模拟库。立即想到两个:
Rhino Mocks是一个享有很高声誉的人。
NMock是另一个。
也有很多商业模拟库可用。编写好的单元测试的一部分实际上是在为它们设计代码,例如,使用有意义的接口,以便我们可以通过实现其接口的"伪造"版本来模拟其依赖对象,而该接口的"伪造"版本行为可预测。方式,用于测试目的。
在数据库模拟中,这意味着将对象"模拟"到自己的数据库访问层,这些对象返回组成表,行或者数据集对象以供单元测试处理的对象。
在我工作的地方,我们通常从头开始制作自己的模拟库,但这并不意味着我们必须这样做。
回答
我们应该做的是从脚本生成的数据库空白副本中运行测试。我们可以运行测试,然后分析数据以确保其具有在运行测试后应具有的功能。然后,我们只需删除数据库即可,因为它是一次性的。这可以全部自动化,并且可以看作是原子动作。
回答
是的,我们应该重构代码以访问可访问数据库的存储库和服务,然后可以模拟或者存根那些对象,以使被测对象永远不会接触数据库。这比存储数据库状态并在每次测试后重置数据库状态要快得多!
我强烈建议Moq作为模拟框架。我用过Rhino Mocks和NMock。 Moq非常简单,可以解决我在其他框架中遇到的所有问题。
回答
如果使用LINQ to SQL作为ORM,则可以即时生成数据库(前提是我们具有用于单元测试的帐户的足够访问权限)。看到http://www.aaron-powell.com/blog.aspx?id=1125
回答
我和这里的其他回答者有相同的问题,并得出了相同的基本结论:不必费心对实际的db通信层进行单元测试,但是如果要对模型函数进行单元测试(以确保它们正在拉数据,正确格式化等),请使用某种虚拟数据源和设置测试来验证要检索的数据。
我也发现单元测试的基本定义不适合许多Web开发活动。但是此页面描述了更多"先进"的单元测试模型,并可能有助于启发一些在各种情况下应用单元测试的想法:
单元测试模式
回答
我在这里解释了一种我一直用于这种情况的技术。
基本思想是在DAL中使用每种方法来断言结果,并且在完成每个测试后,请回滚以便数据库干净(没有垃圾/测试数据)。
我们可能找不到的"唯一"问题是,我通常会进行整个CRUD测试(并非纯粹从单元测试的角度来看),但是此集成测试使我们可以看到实际的CRUD +映射代码。这样,如果发生故障,我们将在启动应用程序之前就知道(当我尝试快速运行时,为我节省了大量工作)