我正在尝试为在其实现中使用数据库查询的函数创建单元测试。我对单元测试的理解是你不应该使用数据库之类的外部资源进行单元测试,你应该创建模拟对象,基本上是对查询结果进行硬编码。
但是,在这种情况下,查询是特定于实现的,如果实现会发生变化,查询也会发生变化。我的理解是单元测试非常有用,因为它基本上允许您随时更改代码的实现,同时确保它仍然有效。
在这种情况下,最好是为测试目的创建数据库,还是为这个特定的实现量身定制测试,如果我们改变实现,就更改测试代码?
答案 0 :(得分:2)
嗯,首先,我认为这很大程度上取决于应用程序环境,QA / dev的技能组合。喜好。所以,我认为正确的做法可能不适合其他人。
说完了......
在我的情况下,我有一个系统,其中一个非常复杂的ERP数据库,我不控制,非常在驱动程序的座位上,我的代码是一个查看器/观察者,而不是该数据库的驱动程序。我没有,并且真的不能使用ORM层,我所有的附加值都在深入理解底层数据库数据模型的查询中。另请注意,我主要是该数据库的查看者,实际上我的代码具有对主数据库的只读访问权限。它确实具有对自己的标记数据库的写访问权,该数据库使用Django ORM并且由于我依赖ORM而在性质上进行测试。
对我来说,最好用数据库进行测试。
模拟对象?如果有很多合理的理由来查看/修改复杂查询的数据库内容,那么模拟就会耗费时间。
更改查询。在我的例子中,经常需要更改和调整那些作为我的应用程序逻辑核心的查询。因此,我需要确保它们按照预期的方式执行实际数据。
多平台问题。我开始在postgresql上编码,调整我的连接库以支持Oracle。完成单元测试并修复弹出的任何错误。数据库抽象是否会识别出Oracle中的LIMIT子句处理等内容?
版本。同样,我不是数据库的主人。因此,随着版本的变化,我需要将代码连接到它。单元测试是非常宝贵的,但那是因为它击中了原始数据库。
测试稳健性。我在学习过程中学到的一个教训是将测试与测试数据库分开。假设您要测试一个功能,该功能可以标记一年内未订购任何内容的活跃客户。我的初始测试方法涉及在测试数据库中进行手动查找,发现CUST701与条件匹配。然后调用我的函数并测试CUST701是否是需要审核的客户的结果集。错误的做法。您要做的是在测试中编写查询,查找未在一年内订购任何内容的活跃客户。根本没有硬编码的CUST701,但您的测试查询查询可以像您想要的那样进行硬编码 - 实际上,它应该尽可能少地查看您的应用程序查询 - 您不希望您的测试SQL复制可能存在的内容生产代码中的错误。一旦您动态识别满足条件的目标客户,然后调用您的测试代码并查看结果是否符合预期。确保您的覆盖工具确定您何时缺少测试方案并在测试数据库中插入这些漏洞。
BDD 。在很大程度上,我开始从BDD角度进行测试,而不是低级TDD。因此,我将调用处理非活动客户列表的URL,而不是测试单个函数。如果总体结果还可以,我有足够的覆盖率,我很好,不用担心详细的低级别来回。因此,在考虑我的答案时也考虑到这一点。
编码器一直有测试数据库。对我来说,利用它们进行BDD /单元测试似乎是合乎逻辑的,而不是假装它们不存在。 但我的核心是一个非常了解Python的SQL编码器,而不是那些碰巧涉及SQL的Python专家。
答案 1 :(得分:1)
由于看起来我得到了错误的结果,我遇到了类似的问题,就像你一样,ORM不是一个选择。
我解决它的方式是使用简单的数据传输对象集合。 所以我写的新代码没有直接访问数据库。它使用简单的对象列表完成所有操作。所有业务逻辑和ui都可以在没有db的情况下进行测试。
然后我有一个其他模块除了读取和写入数据库之外什么都没做,往返于我的对象集合。这是一个穷人的ORM基本上,很多驴工作。 测试运行数据库创建脚本,然后运行一些测试帮助程序代码,用每个测试所需的数据填充数据库。
无聊但有效,你可以稍加谨慎,将其重构到代码库中而不会有太大的风险。