对使用数据库的代码进行单元测试

时间:2008-12-24 11:00:40

标签: unit-testing

我很想知道人们在开发运行数据库的自动化单元测试时采用了什么方法

您是否在运行测试套件之前安装QA数据库(已知起点)。

OR

每当发生数据库调用时,您是否构建了数据库存根?

编辑:相关问题,但不是重复,但对于手头的问题非常重要:How do I unit-test persistence?

11 个答案:

答案 0 :(得分:31)

代表的“数据库存根”通常称为“虚拟存储库”或“模拟存储库”。他们是个好主意。您可以手动编写代码(对于简单的情况不难)或使用像Rhino Mocks这样的框架来生成它们。你没有提到你正在使用的语言.Rhino模拟是为.Net。

如果您使用模拟存储库,那么您可以针对使用数据的代码运行测试,而无需实际使用数据库来存储数据。这使测试运行得非常快,这是一件好事。

当然,您仍然需要在某个阶段测试真实的存储库,这更像是一个问题。这些测试运行速度较慢,因为它们使用真正的数据库。由于速度和依赖性问题,有些人将其归类为“集成测试”而不是单元测试。

我不介意你这么称呼它们,但是将这些测试分开是一个好主意。

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

答案 1 :(得分:12)

如果您正在使用NHibernate,则可以非常轻松test against an in-memory SQLite database非常快。

答案 2 :(得分:3)

我们做了一些技巧:

编辑:我们每个用户都有单独的数据库。我们的构建服务器也有自己的数据库。额外硬盘的成本远远低于影响彼此测试的开发人员的成本。

  1. 我们的应用程序可以自己生成表并执行升级步骤(没有单独的数据库脚本)。这有助于我们的客户,因为他只需要放入一个WAR文件就可以了。应用程序在启动Spring上下文的其余部分之前查看数据库并执行所需的任何DDL语句。
  2. 我们的测试套件有一些代码可以卸载Spring上下文,删除数据库,并使用干净的数据库重新启动上下文。如果我们愿意,我们可以选择关闭此功能
  3. 我们所有的数据库/ SQL单元测试都是Spring事务集成测试。这意味着在测试完成后,事务将被回滚,其他单元测试将再次查看干净的数据库。
  4. 除此之外,我们尽可能地尝试模拟测试,因为单元测试实际上不是集成测试。因此,请尝试从DAO层分离您的服务层。这也可以帮助您更轻松地找到问题,因为您隔离了所有问题。

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

答案 3 :(得分:2)

取决于这种情况,但在遗留系统中,我不想删除数据库,我经常引入一个接口,假设IFooDBManager具有返回ADO.Net实体的方法,如数据表或数据集。当然,它不必是一个接口,但它可以是一个虚拟方法。然后,在我的测试中,我使用了一个很小的API,我在很久以前就建立了一个流畅的界面,我使用它来创建数据集和表,并用测试值填充它们,以便我可以从假货中返回它们。 / p>

流畅的界面如下所示:

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

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

编辑: 我忘了澄清这是用于截断数据库,因此在这种情况下不测试与数据库的交互。实际的交互将在IFooDBManager的具体实现中进行,以测试 完全需要其他东西。

此外,并非所有接口上的方法都会返回,当然还有写入数据库的方法,我通常会在RhinoMocks中通过交互测试来测试,我会对这些方法设置期望。

答案 4 :(得分:2)

实际上,你应该做到这两点。当你说“构建数据库存根”时,暗示了单元测试中的模拟。当你谈到“安装一个QA数据库(已知起始点)”时,这暗示了你真正点击数据库的集成测试。单元测试在游戏中出现得更早,并且是模拟发挥作用的地方。模拟比实际访问数据库要快得多。因此,当您运行许多单元测试时,模拟将节省大量时间。 Rhino Mocks是我个人使用的一个很棒的框架。但是有几个模拟框架,找到最适合你的框架。

在某些时候,您应该通过集成测试来放置您的完整数据库,我找到的最佳方法是使用一个脚本完全创建并使用一组已知数据填充数据库。然后确保除了集成测试之外什么都不会触及新创建的数据库,然后进行测试。

答案 5 :(得分:1)

我们正在使用模拟框架 - 它们模拟数据库,配置文件等资源。使用这些框架是一种有效运行单元测试的实用方法。

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

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

答案 6 :(得分:1)

我通常使用两种方法。

依赖于数据库抽象层的代码可以与IoC一起使用,因此可以使用模拟/存根轻松测试。您有一个 IDataAccess IRepository 界面,您可以测试代码与它的交互。

实际对数据库起作用的代码(比如实现 IDataAccess 的类)是自己测试的,通常执行到数据库的往返(insert-retrieve-update-retrieve-delete) 。在运行每个测试用例之前,将重新创建或清空数据库以避免测试串扰。这导致测试需要几分钟才能运行而不是几秒钟,但在我看来,测试将在生产中使用的实际数据库非常重要。使用内存中的SQLite等替代品不是一个好选择,因为它与SQL Server有太大的不同。

答案 7 :(得分:1)

这是good screen cast on this topic名为小测试的价值

答案 8 :(得分:0)

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

答案 9 :(得分:0)

Spring具有事务性的测试类。播种测试数据,运行测试,回滚测试数据。就好像你从未在那里一样。

答案 10 :(得分:0)

如果您尝试测试数据访问代码,则需要mocking framework。在以下链接中,您可以看到单元测试数据库video tutorial