这个问题:按特定顺序运行 PHPUnit 测试有一个我同意的公认答案,但设计问题在于 PHP 和 PHPUnit。
我正在测试的项目使用 ZF2 和 Doctrine。AbstractHttpControllerTestCase 有一个方法“dispatch”,它实例化一个 ZF2 应用程序并完成创建响应对象的所有步骤。这些测试使用@covers 进行注释,以确保在测试期间运行请求不会覆盖其他方法。请求可能涉及调用视图助手的视图脚本,这些视图助手使用各种服务,因此模拟给定请求期间使用的所有服务变得不可行(并且此代码对于每个测试的复制和维护将变得乏味)。
PHPUnit 能够在单独的进程中运行测试,它通过派生一个新的 PHP 实例并为其提供已编译的代码模板(奇怪的东西)来做到这一点。然后它将包括由 get_included_files() 列出的所有文件,其中包括曾经命中自动加载器的所有文件。即使禁用了 preserveGlobalState,它仍然会包含新流程中所有先前测试所涉及的所有内容。
一些依赖项(通过 composer 安装)使用静态方法、标记为 final 的类或两者兼而有之。静态方法可以被 PHPUnit 模拟,最终类必须使用 Mockery 重载,因为 PHPUnit 将完全拒绝创建最终类的模拟对象。重载类和函数(使用命名空间技巧)必须在单独的进程中完成,以免影响后续测试。到目前为止,一切都很好。
输入一个重载依赖项的测试,以设置对静态方法的期望(在一个可能或可能不会被标记为 final 的类上),或者对尚未实例化的对象设置期望。这只有在之前的测试都没有触及类以重载并设置期望时才有效,否则它将因“无法重新声明类”错误而失败。PHPUnit 试图提供帮助,并在子流程中包含了重新创建测试环境的所有内容,但结果破坏了测试用例。
因此,用例如“@group isolated”标记测试并让这些测试在任何其他测试之前运行而不必调用 PHPUnit 两次(除了不便之外,它会破坏覆盖率分析)将非常有用。
或者,如果有一种方法可以覆盖 PHP 5.5 中已经存在的类,这将允许遭受攻击的测试用例修复其先决条件。但这可能不会发生(runkit 在任何情况下都不是可接受的答案)。