单元测试遗留php应用程序&x2014;如何防止意外的数据库调用

单元测试遗留php应用程序&x2014;如何防止意外的数据库调用,php,mysql,database,unit-testing,phpunit,Php,Mysql,Database,Unit Testing,Phpunit,我正在向使用MySQL兼容数据库的旧式PHP应用程序添加单元测试。我想编写不涉及数据库的真正的单元测试 如何避免意外使用数据库连接 应用程序的许多部分使用静态方法调用来获取对数据库连接包装器对象的引用。如果我正在查看其中一个调用,我知道如何使用依赖项注入和测试加倍来避免从测试中命中数据库,但我不确定如何处理我在任何时候都没有查看的所有数据库查询,这可能是我尝试测试的方法的调用堆栈的一部分 我考虑过在数据库访问类中添加一个public方法,该方法将从PHPUnit引导文件中调用,并设置一个静态变量

我正在向使用MySQL兼容数据库的旧式PHP应用程序添加单元测试。我想编写不涉及数据库的真正的单元测试

如何避免意外使用数据库连接

应用程序的许多部分使用静态方法调用来获取对数据库连接包装器对象的引用。如果我正在查看其中一个调用,我知道如何使用依赖项注入和测试加倍来避免从测试中命中数据库,但我不确定如何处理我在任何时候都没有查看的所有数据库查询,这可能是我尝试测试的方法的调用堆栈的一部分


我考虑过在数据库访问类中添加一个public方法,该方法将从PHPUnit引导文件中调用,并设置一个静态变量以使任何进一步的数据库访问都不可能,但是,我不喜欢纯粹为了测试而在应用程序代码中添加函数,如果在生产中调用,这些测试将是有害的。

将测试添加到遗留应用程序可能很微妙,尤其是单元测试。您可能会遇到的主要问题是,大多数测试都很难编写,并且很容易变得难以理解,因为它们涉及大量的设置和模拟。同时,您可能没有太多的自由来重构它们,因此它们变得更容易测试,因为这将导致代码库中的连锁反应

这就是为什么我通常更喜欢端到端测试。您可以覆盖很多领域,而不必在实现附近进行测试,而且这些测试通常在您以后想要进行大规模重构或迁移遗留代码库时更有用,因为您可以确保您使用的最重要的功能仍能按预期工作

对于这种方法,您需要通过数据库而不是实时数据库进行测试。在开始时,可能只制作一个副本是最容易的,但是从头开始创建一个带有一些测试装置的精简数据库绝对值得。然后,您可以使用selenium之类的工具通过web界面测试您的应用程序,方法是描述您在站点上采取的操作,例如转到url x,填写表单并提交表单,然后描述预期结果,比如我现在应该在url y上,数据库中应该有一个新条目。正如您所看到的,这些类型的测试非常接近您在网站上看到的内容,而不是围绕实现或单个单元编写的。这实际上是有意的,因为在迁移过程中,您可能需要删除大块并重写它们。单元测试将变得完全无用,因为实现可能会发生巨大的变化,但是那些描述站点功能的end2end测试仍然有效

你可以用多种方法来解决这个问题。如果您熟悉PHPUnit,您可能希望尝试selenium扩展。您应该在网上找到这方面的教程,例如:

此类测试的另一个流行选项是使用。在这两种情况下,最困难的部分是设置selenium,但一旦您能够编写一个简单的测试,例如,它就会转到frontpage,检查一些文本片段并运行该测试。您可以非常快速地编写测试

这些测试的一大缺点是速度非常慢,因为它们执行完整的web请求,并且在某些情况下必须执行一些等待JavaScript的操作。因此,您可能不应该测试所有内容。相反,试着专注于最重要的特性。如果你有一些电子商务项目,可能会通过一个非常通用的结帐程序。然后展开对您来说很重要的不同变体,例如登录用户与新用户或向购物篮中添加凭单。另一个好的开始方法是编写非常愚蠢的测试,只检查url是否实际可访问,所以转到url并检查状态代码和一些预期的文本片段。在确保应用程序正确运行方面,这些并不是很有用,但它们仍然可以为您提供一些安全性,以确定是否突然出现了一些随机的500个错误


这可能是确保应用程序安全运行的最佳方法,并使升级、重构或迁移应用程序或其部分变得更容易。此外,每当您添加新特性时,请尝试为它们编写一些实际的单元测试。如果它们与代码的旧部分没有太多的联系,这可能是最简单的。在最好的情况下,您不必太担心数据库,因为您可以将从数据库获得的数据替换为您在测试中准备的一些实例,然后只测试任何功能。显然,如果是一个简单的表单,我们希望有一个表单将这些数据添加到数据库中,那么您可能仍然不想编写单元测试,而是要编写一个更大的端到端测试。

单元测试中的数据库调用有什么问题?如果该单元的功能是从数据库中读取或写入数据,则需要这样做。如果不是,数据库不应该成为问题。看看框架是如何做到这一点的:通常,它们使用从开发模式中克隆出来的测试数据库模式来验证功能。删除整个数据库将是一项艰巨的任务,可能是一个更大的错误。@tadman单元测试是一种测试,它将一个代码单元与其所有依赖项隔离开来。与数据库交互的测试不是数据库。@bdsl在事实发生后将单元测试添加到遗留代码库通常至少是痛苦的,最坏情况下是不可能的。我建议将特性测试作为集成测试或结束测试来实现,以便对遗留应用程序进行测试。从那里你可以把它重构成更小的、可管理的、可以进行单元测试的模块。@SebastianBergmann这取决于你定义的严格程度