当我看到许多用于测试的内存数据库实现时,我真的很困惑,因为我还从集成测试最佳实践中听到很多信息,即运行测试的环境应尽可能类似于生产环境,包括操作系统,库,数据库引擎等。
我在这里想念什么?
当我看到许多用于测试的内存数据库实现时,我真的很困惑,因为我还从集成测试最佳实践中听到很多信息,即运行测试的环境应尽可能类似于生产环境,包括操作系统,库,数据库引擎等。
我在这里想念什么?
Answers:
在典型的软件开发情况下,在两个阶段使用测试:在开发过程中以及沿着开发链移动产品之前。
第一种情况是在开发过程中运行测试,它具有短期目标:定义任务(如TDD:编写失败的测试,然后使其通过),防止回归,确保所做的更改不会破坏其他内容,等等。测试必须非常快:理想情况下,整个测试套件的运行时间不到5秒,并且您可以在编写代码时在IDE或文本编辑器旁边的循环中运行它。您引入的任何回归都会在几秒钟内弹出。在这个阶段,快速的测试运行比捕获100%的回归和错误更为重要,并且由于在生产系统的精确副本上进行开发是不切实际的(或完全不可能),因此在此处进行完美测试所需的精力并不值得它。使用内存数据库是一种折衷方案:它们不是生产系统的精确副本,但它们确实有助于将测试运行时间限制在5秒以下;如果选择是在与我的数据库相关测试的数据库设置稍有不同,还是根本没有测试之间进行选择,那么我知道我选择了什么。
第二种情况是,沿着开发链移动代码确实需要进行大量测试。因为我们可以(并且应该)使开发过程的这一部分自动化,所以我们可以提供更慢的测试-即使一次完整的测试运行需要几个小时,安排夜间构建仍然意味着我们始终可以准确了解昨天的代码库。现在,尽可能精确地模拟生产环境非常重要,但是我们可以负担得起。因此,我们不会在内存数据库中进行权衡:我们将与生产系统完全相同的DBMS版本安装到完全相同的DBMS中,并且如果可能的话,我们在测试开始之前用实际的生产数据填充它。
我想这是权衡速度/与环境相称的。测试应该经常运行,这意味着它们必须很快。尤其是单元测试,该测试不应超过几秒钟。
集成测试的运行速度会较慢,但运行速度很快时,您可以更频繁地运行它们。例如,每次提交之前。当然,它并不像完整的环境那样完整,但是至少您正在测试映射层,生成的SQL,零件之间如何通信等。对于昂贵的数据库,您还应确保不要不需要为每个人购买许可证。每小时运行一次测试可能覆盖90%的代码,而不是每天或最糟糕的一周一次覆盖100%的代码。
话虽如此,您当然需要使用真实的数据库和完全集成的环境进行测试。您可能不会经常运行这些测试,但是由于您先前的测试已经给您带来了信心,因此剩下的只是怪异的平台特定错误。
对于进行简单的测试,完全可以接受对数据库访问层的模拟。您调用getName()
,它调用被嘲笑的DAO,并为名字返回“ John”,为姓返回“ Smith”,将它们组装起来,一切都非常完美。无需在那里实际对数据库进行单元测试。
当逻辑变得更加复杂时,事情就会变得更加复杂。如果您有方法“ createOrUpdateUser(...)”怎么办。如果模拟了数据库,则可以在模拟不返回任何对象时验证给定方法是否已使用某个参数调用一次,并且当模拟返回不存在的对象时将在数据库上调用另一个方法。这开始到那条模糊的线,在该线可能更容易(特别是如果已经存在)启动专门的内存数据库并使用预配置的数据测试该代码。
在我工作的一些实际代码中(销售点),我们有一种resumeSuspededTransaction(...)
方法。这会将事务从数据库中拉到一个对象(及其组件)中并更新数据库。我们对它进行了嘲笑,并且将代码进行了序列化和反序列化处理,从而在代码中潜伏了一个错误(对更改后的类型进行了序列化)。
模拟没有向我们显示错误,因为它正在返回其满意的路径-序列化事务,将其存储在模拟中,从模拟中反序列化它,测试它们是否相等。但是,当您将带有前导零的对象序列化到数据库时,它将删除它们,然后将其重新组合成不包含零的字符串。通过故障排除,我们在没有数据库的情况下捕获了该错误(一旦知道它的存在就不难发现)。
后来,我们在该数据库中放置了一个数据库,并意识到如果我们转而使用内存数据库,则该bug永远不会通过该junit测试获得。
内存数据库具有以下优点:
用外行的话来说:
对于单元测试而言,对架构的重要部分进行模拟是可以的(也是必须的)。
但是对于集成测试,我非常同意。不应进行模拟,应提供与真实环境尽可能相似的环境。
毕竟,集成测试是关于测试架构的不同部分如何协同工作的。