【问题标题】:PHPUnit not continuing with tests after fatal error when using --process-isolation使用 --process-isolation 时出现致命错误后 PHPUnit 无法继续测试
【发布时间】:2012-04-09 17:52:19
【问题描述】:

我有一个 PHPUnit 测试套件,由于未找到类定义,该套件目前正在导致致命错误。归根结底,这是测试代码本身的失败,以及开发人员在提交代码之前未能证明测试本身的正确性。

但是,这样的事情确实时有发生,如果发生致命错误(无论最终由谁负责),测试被简单地标记为失败,而测试的其余部分,那就太好了套件仍然被执行。

我已阅读有关--process-isolation 开关的信息,据我所知,它应该 解决这个问题。由于每个测试都在单独的进程中运行,如果子进程因致命错误而死亡,父进程仍然可以继续运行。事实上,这在对类似问题的回答中明确说明了这一点:https://stackoverflow.com/a/5340151/84762,它显示了我希望自己看到的确切输出类型。

但是,无论我是否使用 --process-isolation 标志,我似乎都能得到完全相同的输出:

没有进程隔离

[kogi@phagocyte ~]$ /usr/bin/phpunit --colors --verbose --coverage-html "target/coverage" ~/app/zend/tests/application/

PHP Fatal error:  Class 'Rmd_Database_OldObject' not found in /home/kogi/app/zend/private/models/translate/Poll.php on line 9
PHP Stack trace:
PHP   1. {main}() /usr/bin/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
PHP   3. PHPUnit_TextUI_Command->run() /usr/share/pear/PHPUnit/TextUI/Command.php:130
PHP   4. PHPUnit_Runner_BaseTestRunner->getTest() /usr/share/pear/PHPUnit/TextUI/Command.php:150
PHP   5. PHPUnit_Framework_TestSuite->addTestFiles() /usr/share/pear/PHPUnit/Runner/BaseTestRunner.php:96
PHP   6. PHPUnit_Framework_TestSuite->addTestFile() /usr/share/pear/PHPUnit/Framework/TestSuite.php:419
PHP   7. PHPUnit_Util_Fileloader::checkAndLoad() /usr/share/pear/PHPUnit/Framework/TestSuite.php:358
PHP   8. PHPUnit_Util_Fileloader::load() /usr/share/pear/PHPUnit/Util/Fileloader.php:79
PHP   9. include_once() /usr/share/pear/PHPUnit/Util/Fileloader.php:95
PHP  10. require_once() /home/kogi/app/zend/tests/application/translate/PollTest.php:11

Fatal error: Class 'Rmd_Database_OldObject' not found in /home/kogi/app/zend/private/models/translate/Poll.php on line 9

Call Stack:
    0.0003      91584   1. {main}() /usr/bin/phpunit:0
    0.0076     612672   2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
    0.0076     613744   3. PHPUnit_TextUI_Command->run() /usr/share/pear/PHPUnit/TextUI/Command.php:130
    0.0246    1249464   4. PHPUnit_Runner_BaseTestRunner->getTest() /usr/share/pear/PHPUnit/TextUI/Command.php:150
    0.0706    1626680   5. PHPUnit_Framework_TestSuite->addTestFiles() /usr/share/pear/PHPUnit/Runner/BaseTestRunner.php:96
    0.1691    8053584   6. PHPUnit_Framework_TestSuite->addTestFile() /usr/share/pear/PHPUnit/Framework/TestSuite.php:419
    0.1693    8057320   7. PHPUnit_Util_Fileloader::checkAndLoad() /usr/share/pear/PHPUnit/Framework/TestSuite.php:358
    0.1694    8057664   8. PHPUnit_Util_Fileloader::load() /usr/share/pear/PHPUnit/Util/Fileloader.php:79
    0.1711    8240600   9. include_once('/home/kogi/app/zend/tests/application/translate/PollTest.php') /usr/share/pear/PHPUnit/Util/Fileloader.php:95
    0.1805    9187768  10. require_once('/home/kogi/app/zend/private/models/translate/Poll.php') /home/kogi/app/zend/tests/application/translate/PollTest.php:11

WITH进程隔离

[kogi@phagocyte ~]$ /usr/bin/phpunit --colors --verbose --coverage-html "target/coverage" --process-isolation ~/app/zend/tests/application/
PHP Fatal error:  Class 'Rmd_Database_OldObject' not found in /home/kogi/app/zend/private/models/translate/Poll.php on line 9
PHP Stack trace:
PHP   1. {main}() /usr/bin/phpunit:0
PHP   2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
PHP   3. PHPUnit_TextUI_Command->run() /usr/share/pear/PHPUnit/TextUI/Command.php:130
PHP   4. PHPUnit_Runner_BaseTestRunner->getTest() /usr/share/pear/PHPUnit/TextUI/Command.php:150
PHP   5. PHPUnit_Framework_TestSuite->addTestFiles() /usr/share/pear/PHPUnit/Runner/BaseTestRunner.php:96
PHP   6. PHPUnit_Framework_TestSuite->addTestFile() /usr/share/pear/PHPUnit/Framework/TestSuite.php:419
PHP   7. PHPUnit_Util_Fileloader::checkAndLoad() /usr/share/pear/PHPUnit/Framework/TestSuite.php:358
PHP   8. PHPUnit_Util_Fileloader::load() /usr/share/pear/PHPUnit/Util/Fileloader.php:79
PHP   9. include_once() /usr/share/pear/PHPUnit/Util/Fileloader.php:95
PHP  10. require_once() /home/kogi/app/zend/tests/application/translate/PollTest.php:11

Fatal error: Class 'Rmd_Database_OldObject' not found in /home/kogi/app/zend/private/models/translate/Poll.php on line 9

Call Stack:
    0.0003      91752   1. {main}() /usr/bin/phpunit:0
    0.0076     612824   2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
    0.0076     613896   3. PHPUnit_TextUI_Command->run() /usr/share/pear/PHPUnit/TextUI/Command.php:130
    0.0246    1250360   4. PHPUnit_Runner_BaseTestRunner->getTest() /usr/share/pear/PHPUnit/TextUI/Command.php:150
    0.0708    1627528   5. PHPUnit_Framework_TestSuite->addTestFiles() /usr/share/pear/PHPUnit/Runner/BaseTestRunner.php:96
    0.1688    8054296   6. PHPUnit_Framework_TestSuite->addTestFile() /usr/share/pear/PHPUnit/Framework/TestSuite.php:419
    0.1690    8057992   7. PHPUnit_Util_Fileloader::checkAndLoad() /usr/share/pear/PHPUnit/Framework/TestSuite.php:358
    0.1691    8058336   8. PHPUnit_Util_Fileloader::load() /usr/share/pear/PHPUnit/Util/Fileloader.php:79
    0.1707    8241296   9. include_once('/home/kogi/app/zend/tests/application/translate/PollTest.php') /usr/share/pear/PHPUnit/Util/Fileloader.php:95
    0.1801    9188464  10. require_once('/home/kogi/app/zend/private/models/translate/Poll.php') /home/kogi/app/zend/tests/application/translate/PollTest.php:11

对于我们这些无法在头脑中有效区分的人来说,这两个输出实际上是相同的(除了执行时间和内存使用情况几乎可以忽略不计)。

在这两种情况下,致命错误都会杀死整个测试套件。在这种特殊情况下,这发生在第三个测试中,其余 150 个测试(在其他几个文件/套件中)从未执行。

我在这里做错了什么?是否有其他方法可以在一个测试中出现致命错误(将测试标记为失败)并继续执行剩余的测试?


编辑

我正在使用 PHPUnit 3.6.10

编辑

对这个问题的答案的评论激发了 PHPUnit 的 GitHub 页面上的一张新票:https://github.com/sebastianbergmann/phpunit/issues/545

【问题讨论】:

  • 您会在这里找到答案:stackoverflow.com/questions/277224/…
  • 我不确定你是指说他们不能被抓住的部分还是说他们不应该被抓住的部分......但两种说法都是错误的。在这种情况下,当测试系统的功能时,在不终止其余测试过程的情况下捕获致命错误非常重要。如果我误解了您的回答,我深表歉意,请告诉我。
  • 我指的是说它不能被抓住的部分。确实,您可以使用 register_shutdown_function 来打印要记录的内容并这样做“清理”,但是由于框架已经被破坏(在堆栈上),我不相信您可以“恢复”您的程序。既然您确切知道在哪一行触发了致命错误 - 为什么不修复它呢?
  • 感谢您的澄清。关键不是修复它。在这种情况下,这是微不足道的。关键是防止由于单个开发人员的错误提交而无法运行剩余的测试。如果有人提交了一个糟糕的测试,它不应该禁用整个系统。我知道致命错误是......致命的。你无法从他们身上恢复过来。但据我了解,PHPUnit 的 --process-isolation 函数将每个单独的测试函数分叉到一个单独的子进程中。如果那个孩子有一个致命错误,它就无法恢复,但父母仍然可以将它标记为失败的测试并继续下一个
  • 现在我明白了 :) 根据这个:stackoverflow.com/questions/3841190/… 你应该在一个新的过程中启动 每个测试套件 希望它有所帮助。

标签: php unit-testing error-handling continuous-integration phpunit


【解决方案1】:

PHPUnit 在运行任何测试之前加载将要运行的每个测试文件。这会导致 PHP 解析这些文件并执行它们的顶级代码。如果加载了任何类,例如,扩展了一个不存在的类,您将收到致命错误。

如果不增强 PHPUnit 来解析文件而不在扫描过程中执行它们的代码,我看不到任何解决方法。

【讨论】:

  • 使用--process-isolation时还是这样吗?
  • 是的,我通过更改工作测试来扩展一个错误命名的类来运行测试,即使隔离它也会停止。
  • 该死的!回到原点!谢谢。
  • 这主要是 PHP 的设计结果。在这种语言中,某些错误被认为是“致命的”,而在其他错误中仅仅是“例外”。 Java 中的ClassNotFoundException 可以被捕获、记录和绕过,而此错误在 PHP 中是不可恢复的。我们不得不重写一些 PHPUnit 的行为,以允许测试在面对其他致命错误时继续进行,例如@covers 引用无效类/方法的注释。
  • 感谢您提供的信息。我想知道作者是否愿意让 PHPUnit 在子进程而不是父进程中加载​​每个测试文件……父进程可以找到所有要使用的文件,然后生成子进程(如果使用 --process-isolation)加载文件(解析/执行顶级代码)并运行测试。这将允许所有测试运行,即使其中一些测试存在致命错误。
猜你喜欢
  • 1970-01-01
  • 2012-04-06
  • 2016-10-06
  • 1970-01-01
  • 2011-04-06
  • 2017-02-09
  • 1970-01-01
  • 2014-05-08
  • 2020-01-25
相关资源
最近更新 更多