单元测试-数据库耦合应用


15

在对集成到与数据库紧密耦合的应用程序中的模型进行单元测试的最佳方法是什么?

这里的特定情况是一个购物车-我希望能够测试购物车中商品的添加,删除和检索以及定价逻辑等。在我看来,这一切都需要数据库访问,尽管我已经读过好几次了。应该避免数据库访问。


1
有趣的是,有效回答“重写应用程序代码”的答案已被投票
AD7six 2012年

Answers:


10

依赖注入是处理此问题的一种方法。您可以设置一个测试数据库来模仿购物车,甚至可以编写一些“确认”客户交易的代码。然后在运行时,您的软件将选择要连接的组件。

只是在测试过程中什么都不要连接到生产数据库!


1
借助DI和适当的应用程序设计,您应该能够在没有任何数据库的情况下进行测试---只要您注入的模拟能够提供足够详细的后端数据库模拟即可。
Peter K.

4

在单元测试中,您必须定义测试对象的边界。单元测试不同于集成测试。如果定价逻辑独立于购物车内容,则您将对其进行单独测试。如果不是这种情况,并且所有模块都紧密耦合,请构建一个尽可能模拟生产的测试环境,并使用该环境。从长远来看,我不认为捷径和模拟会有所帮助。


2

该模型不应依赖于(具体的)数据库。如果仅知道传递给模型的抽象数据库(读取为“接口”),则可以用模拟对象替换该数据库。

面向对象的编程中模拟对象是模拟对象,它们以受控方式模拟实际对象的行为。程序员通常会创建一个模拟对象来测试其他对象的行为,就像汽车设计师使用碰撞测试假人模拟人类在车辆撞击中的动态行为一样。


1

我有一个类似的问题-我无法保证我的测试数据库保留这些值。因此,将来我会得到其他价格。

我将所需的数据提取到一个小的sqlite -DB中,并将该数据库用于测试。现在,Test-DB是我的单元测试设置的一部分。


2
单元测试的重点是隔离测试您的代码。如果使用sqllite数据库,则它不是孤立的。数据库之间的不一致也会导致错误
Tom Squire

0

“最佳”是主观的,但是您可以仅使用测试数据库连接。

使用固定装置加载一些测试数据(要购买的示例产品),然后为要测试的类/功能编写测试用例。


将单元测试描述为测试作为集成测试作用在数据库上的功能的单元测试相当误导@murph。
AD7six 2012年

1
好的,现在我很困惑-如果它涉及一个数据库,就大多数定义而言,它不是单元测试,因为它不是自包含的。如果您有一个数据库,那么您将在更高级别上运行测试,该测试具有依赖项,而依赖项则负责“组合”事物。无论如何,这对我来说如何解决问题都不是一个清晰的解释。
Murph 2012年

0

为Symfony 1.4(PHP)构建了一个插件来解决此问题(以及其他问题)。它是按照Django测试框架(Python)的运行方式建模的:该框架在每次测试开始之前先构建并填充一个单独的测试数据库,并在每次测试完成后破坏测试数据库。

我在性能(如果架构没有改变,为什么不简单地清除数据而不是重建整个结构?)和便利性(有时我想在数据库运行后检查数据库)方面对此策略有一些担忧。测试失败,因此请不要随意破坏它!),所以我采取了一种略有不同的方法。

如果自上次测试以来发生了模型更改,则在第一个测试运行之前,将销毁并重建数据库。在每个后续测试运行之前,将清除数据库中的数据,但是不会重建结构(尽管可以根据需要从测试中触发手动重建)。

通过有选择地在每个测试中加载数据夹具,可以为该测试创建合适的环境,而不会干扰后续测试。夹具文件也可以重新使用,这使这项任务的繁琐程度降低了(尽管它仍然是我编写测试时最不喜欢的部分!)。

在两个测试框架中,数据库适配器都配置为使用测试连接而不是“生产”连接,以防止测试执行破坏现有数据。


0

我想说的就是继续并使用固定装置来预加载数据。当测试数据的操作时,这就是单元测试框架似乎通常如何工作的方式。

但是,如果您真的想避免必须连接到任何种类的数据库,并且过分严格的定义(即单元测试不会触及代码外的任何内容),请看一下对象模拟-它可能会给您带来一些想法。

例如,与其直接将SQL拖放到所需的代码中,不如通过一种方法来调用仅执行SQL所执行的方法。使用Person.getPhoneNumber(),例如,而不是SELECT phone_number FROM person WHERE id = <foo>。不仅一目了然,更加清晰易懂,而且在测试过程中,您可以模拟Person对象,以便getPhoneNumber()始终返回555-555-5555或类似内容,而无需接触数据库。


0

如果使用junit,这很容易做到。

“设置”应定义并填充一组临时表。

然后,您可以对所有更新,插入,删除功能执行单元测试。

对于每个测试,您都调用update方法,然后运行一些SQL来验证预期结果。

在“拆卸”阶段中,您将所有表都删除。

这样,您始终可以对相同的初始数据运行相同的测试。如果您将表保留在测试之间,那么它们最终也会因失败的测试而“被污染”,因此几乎不可能进行一致的“插入”测试,因为您需要在每个测试中不断发明新的密钥。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.