在对集成到与数据库紧密耦合的应用程序中的模型进行单元测试的最佳方法是什么?
这里的特定情况是一个购物车-我希望能够测试购物车中商品的添加,删除和检索以及定价逻辑等。在我看来,这一切都需要数据库访问,尽管我已经读过好几次了。应该避免数据库访问。
在对集成到与数据库紧密耦合的应用程序中的模型进行单元测试的最佳方法是什么?
这里的特定情况是一个购物车-我希望能够测试购物车中商品的添加,删除和检索以及定价逻辑等。在我看来,这一切都需要数据库访问,尽管我已经读过好几次了。应该避免数据库访问。
Answers:
我有一个类似的问题-我无法保证我的测试数据库保留这些值。因此,将来我会得到其他价格。
我将所需的数据提取到一个小的sqlite -DB中,并将该数据库用于测试。现在,Test-DB是我的单元测试设置的一部分。
“最佳”是主观的,但是您可以仅使用测试数据库连接。
使用固定装置加载一些测试数据(要购买的示例产品),然后为要测试的类/功能编写测试用例。
我为Symfony 1.4(PHP)构建了一个插件来解决此问题(以及其他问题)。它是按照Django测试框架(Python)的运行方式建模的:该框架在每次测试开始之前先构建并填充一个单独的测试数据库,并在每次测试完成后破坏测试数据库。
我在性能(如果架构没有改变,为什么不简单地清除数据而不是重建整个结构?)和便利性(有时我想在数据库运行后检查数据库)方面对此策略有一些担忧。测试失败,因此请不要随意破坏它!),所以我采取了一种略有不同的方法。
如果自上次测试以来发生了模型更改,则在第一个测试运行之前,将销毁并重建数据库。在每个后续测试运行之前,将清除数据库中的数据,但是不会重建结构(尽管可以根据需要从测试中触发手动重建)。
通过有选择地在每个测试中加载数据夹具,可以为该测试创建合适的环境,而不会干扰后续测试。夹具文件也可以重新使用,这使这项任务的繁琐程度降低了(尽管它仍然是我编写测试时最不喜欢的部分!)。
在两个测试框架中,数据库适配器都配置为使用测试连接而不是“生产”连接,以防止测试执行破坏现有数据。
我想说的就是继续并使用固定装置来预加载数据。当测试数据的操作时,这就是单元测试框架似乎通常如何工作的方式。
但是,如果您真的想避免必须连接到任何种类的数据库,并且过分严格的定义(即单元测试不会触及代码外的任何内容),请看一下对象模拟-它可能会给您带来一些想法。
例如,与其直接将SQL拖放到所需的代码中,不如通过一种方法来调用仅执行SQL所执行的方法。使用Person.getPhoneNumber()
,例如,而不是SELECT phone_number FROM person WHERE id = <foo>
。不仅一目了然,更加清晰易懂,而且在测试过程中,您可以模拟Person对象,以便getPhoneNumber()
始终返回555-555-5555
或类似内容,而无需接触数据库。