忽略PHPUnit中的exit()和die()函数

8
首先,标题会让人认为这是这个这个的副本,但出于几个原因,即使我的初始问题相同,那些答案对我不起作用。我会解释原因。
我的问题是:在我的代码中,我有几个场合需要发送一个标题和正文,然后终止处理。与那些其他问题不同,我不能使用return或抛出异常(它们显然是设计用于不同目的而不是exit的不同函数,并且这不是错误;仅仅是一种特定情况下的早期运行时终止)。
尽管如此,我仍然希望编写单元测试来运行这些方法,确保设置了适当的标题(在这里找到了解决方案),输出正文是正确的(通过测试用例中的$this->expectOutputString()方法解决),然后继续测试。在此期间,将发生exit
我尝试了PHPUnit中的@runInSeparateProcess注释,还检查了test_helpers扩展,它有效,但我不想为一个单独的原生PHP代码行添加另一个扩展(请注意,测试也将在生产环境中运行),这会导致所有东西都崩溃。必须有一种简单的方法而不牺牲最佳实践。
有没有人有解决这个问题的好方法?

你找到任何解决方案了吗? - gmponos
3个回答

4

我在引导程序中添加了一个变量,这样在代码中就可以使用 IF 语句引用它,在那些极少数需要不退出的情况下。

define ('PHPUNIT_RUNNING', 1); 

普通程序:

if(! @PHPUNIT_RUNNING === 1 )
{
    exit;
}

PHPUnit没有被定义为规则,因此在PHP执行时会生成警告(我们用@隐藏)。当不处于测试模式时,代码将按我们的意愿执行。这是在主要代码编写之后添加的,因为我们将PHPUnit测试添加到现有项目中,而不是进行TDD。

请注意:

我们尽可能地少做这样的操作来解决遗留问题,否则我们会像其他人建议的那样抛出异常或将数据返回给父函数。


感谢您的回复,Steven。是的,我的备选方案就是使用测试环境变量来解决这个问题,但我不喜欢为了让测试工作而编写特殊代码(if(!Environment::isTesting()) { exit; })。目前,我正在使用test_helpers,它允许我运行特定方法set_exit_overload(),以基本上将exit重载为任何函数(例如空函数)。 - Helge Talvik Söderström
我也很讨厌这个问题,但不幸的是,我想不到更简单的解决方法。我们旧的代码库导致人们在编码时采用了退出而不是尝试返回或抛出异常,这使得代码更加过程化而非真正的面向对象。 - Steven Scott

2

我刚刚测试了以下想法:

ExitException.php:

<?php
class DieException extends Exception {} // for those people who like die as well as exit
class ExitException extends Exception {}

entrypoint.php:

<?php
require_once 'ExitException.php';

try {
  require 'main_code.php';
  run_main_code();
}
catch (DieException $e) {
  return $e->getMessage();
}
catch (ExitException $e) {
  return $e->getMessage();
}

几个 include 后:
深度代码 (deepcode.php)
<?php
throw new ExitException('Exiting normally');

这模拟了一个在最高层级捕获的exitdie。你的程序将像之前那样退出,并且现在可以在不杀死整个测试套件的情况下进行测试。唯一需要注意的是,如果你的现有代码中也捕获了其他普通的Exception,那么你需要修改你的代码来重新抛出DieException/ExitException,直到它们达到顶层。

另一种选择是干净地返回,这可能需要重写你的大部分代码。也就是说,如果在那个点上没有退出,为什么你的程序会生成进一步的输出呢?它应该尽早检查是否需要“提前退出”,然后只调用那段代码并流经程序的自然结束点。

如果你必须处理第三方库或其他不能或不应该更改的代码,那么这个问题变得更加困难,但除非它们有自己的单元测试,否则编写它们的测试并没有价值,因为理想情况下,你不应该更改第三方代码,以避免未来的兼容性问题。一个编写良好的第三方库永远不应该die。它应该始终将控制权返回给调用程序,或抛出可以被捕获的Exception


0

exit;是一种确保线程停止的简单方法,但它不会让你处于一个良好的单元测试的位置。

exit;包装在一个类中的函数中,你可以在单元测试中进行模拟。 在被测试的代码中调用该函数,而不是exit;。 然后的问题是你需要添加一个return;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接