对使用数据库的代码进行unit testing

我很想知道人们在开发运行数据库的自动unit testing时采取了哪些方法

在testing套件运行之前,您是否安装了QA数据库(已知的起点)?

要么

数据库调用发生时,是否build立数据库存根?

编辑:相关的问题,但不是重复的,虽然对手头的事情相当重要: 如何unit testing持久性?

站在“数据库存根”通常被称为“假存储库”或“模拟存储库”。 他们是个好主意。 你可以手工编码(不是简单的情况下),也可以使用像Rhino Mocks这样的框架来生成它们。 你没有提到你在用什么语言。犀牛嘲笑是.Net。

如果使用模拟存储库,则可以针对与数据一起使用的代码运行testing,而不实际使用数据库作为数据。 这使得testing运行非常快,这是一件好事。

当然,你还需要在某个阶段testing真正的存储库,这是一个问题。 这些testing运行速度会比较慢,因为它们使用真正的数据库。 由于速度和依赖性问题,有些人会将其归类为“集成testing”而不是unit testing。

我不介意你这么称呼他们,但把这些testing分开是个好主意。

数据一致性的一个好主意是在testing之前开始一个db事务,然后再回滚。 这样数据库状态就恢复到了testing前的状态。

如果你使用的是NHibernate,你可以很容易地testing内存中的SQLite数据库 ,这个数据库 非常快。

我们做一些技巧:

编辑:我们有每个用户分开的数据库。 我们的buildserver也有它自己的数据库。 额外硬盘的成本远低于影响彼此testing的开发者的成本。

  1. 我们的应用程序可以生成表格,并自己执行升级步骤(没有单独的数据库脚本)。 这有助于我们的客户,因为他只需要放入一个WAR文件,他就完成了。 应用程序查看数据库,并在启动剩余的Spring上下文之前执行所需的任何DDL语句。
  2. 我们的testing套件有一些代码可以卸载Spring上下文,删除数据库,并用干净的数据库重新启动上下文。 如果我们喜欢的话,我们可以select性地closures它
  3. 我们所有的数据库/ SQLunit testing都是Spring事务集成testing。 这意味着在testing完成之后,事务被回滚,其他的testing单元将再次查看干净的数据库。

除此之外,我们尝试尽可能地进行模拟testing,因为unittests实际上并不是要进行集成testing。 所以尝试从DAO层分离你的服务层。 这也可以帮助你更容易地发现问题,因为你隔离了一切。

在某些时候,我相信你需要访问数据库,因为这不是我们生活的理想/学术世界:-)

取决于案例,但是在遗留系统中,我不希望存入数据库,我经常会介绍一个接口,可以说IFooDBManager具有返回ADO.Net实体(如数据表或数据集)的方法。 当然,它不一定是一个接口,但它可能是一个虚拟的方法,例如。 然后,在我的testing中,我使用了一个很早以前就build立起来的stream畅接口的小型API,我用它来创build数据集和表格,并用testing值填充它们,这样我就可以从我的假货中返回它们。

stream畅的界面如下所示:

return DataTableBuilder.Create() .DefineColumns("a, b") .AddRow().SetValue("a", 1).SetValue("b", 2).DoneWithRow() .AddRow().SetValue("a", 10).SetValue("b", 20).DoneWithRow() .Table 

正如我所说,这只是我使用的方法之一,这主要是遗留系统,我不想在框架作品等等引入新的依赖关系。 然而,这是一个我没有看到很多其他人使用的技术,所以我认为这是值得一提的。

编辑:我忘了澄清这是用于残留数据库,所以在这种情况下,与数据库的交互没有testing。 实际的交互将发生在IFooDBManager的具体实现中,以testing完全需要的东西。

此外,并不是所有的方法在这样的接口返回的东西,当然也有写入数据库的方法,这些我通常在RhinoMocks中通过交互testing进行testing,我在这些方法上设置了期望值。

事实上,你应该两个都做。 当你说“构build数据库存根”时,这暗示了在unit testing中的嘲弄。 而当你谈到“安装QA数据库(已知起点)”时,这暗示了你实际上碰到数据库的集成testing。 unit testing在游戏中得到了更早的地方,并且是嘲弄的地方。 嘲笑比实际上击中数据库要快得多。 所以当你运行许多unit testing时,嘲笑会节省很多时间。 Rhino Mocks是我亲自使用的一个很好的框架。 但有几个嘲讽的框架,find最适合你的一个。

在某些时候,您应该通过集成testing来放置完整的数据库,而我发现的最好的方法是使用一个脚本完全创build并使用已知的一组数据填充数据库。 然后确保除了集成testing之外没有任何东西会触及新创build的数据库,然后试验。

我们正在使用模拟框架 – 他们嘲笑像数据库,configuration文件等资源。使用这些框架是一种有效运行你的unit testing的实用方法。

我们正在开发的框架之一就是Rhino Mocks。 你可以在下面的链接中阅读。 它也很好地介绍了为什么你需要这样的框架。

http://weblogs.asp.net/stephenwalther/archive/2008/03/22/tdd-introduction-to-rhino-mocks.aspx

我通常使用两种方法。

依赖于数据库抽象层的代码可以与IoC协同工作,因此可以通过mocks / stub轻松进行testing。 您有一个IDataAccessIRepository接口,并testing您的代码与它的交互。

对数据库实际工作的代码(比如一个实现IDataAccess的类)是自己testing的,通常执行数据库往返(insert-retrieve-update-retrieve-delete)。 在运行每个testing用例之前,都要重新创build或清空数据库,以避免testing串扰。 这导致testing需要几分钟的时间来运行,而不是几秒钟,但在我看来,testing您将用于生产的实际数据库是非常重要的。 使用诸如内存中的SQLite之类的替代品并不是一个好的select,因为它与SQL Server差别太大了。

这个题目叫做“小testing的价值” 这个主题

在我的数据库testing实践中,我使用了NUnit,在执行整个testing序列之前,数据库被安装并填充了testing数据。 当然,这个过程并不是很快,但是在testing运行的时候阻碍了你做其他工作。

Spring有事务性的testing类。 播种testing数据,运行testing,回滚。 就好像你从来没有在那里。

如果您要testing数据访问代码,则需要一个模拟框架 。 在以下链接中,您可以看到一个unit testing数据库video教程