42

本质上,我有一个名为 killProgram 的类的方法,它旨在发送一个 hTTP 重定向,然后杀死 PHP。

我应该如何测试这个?当我运行 phpunit 时,它不会为该测试返回任何内容,并完全关闭。

现在我正在考虑让 killProgram 函数抛出一个不应被处理的异常,这将允许我断言抛出了一个异常。

有没有更好的办法?

4

6 回答 6

28

这显然是一个老问题,但我的建议是将die()'s 的代码移动到一个单独的方法中,然后您可以模拟。

例如,而不是这样:

class SomeClass
{
    public function do()
    {
        exit(1);
        // or
        die('Message');
    }
}

做这个:

class SomeClass
{
    public function do()
    {
        $this->terminate(123);
        // or
        $this->terminate('Message');
    }

    protected function terminate($code = 0)
    {
        exit($code);
    }

    // or 
    protected function terminate($message = '')
    {
        die($message);
    }
}

这样您就可以轻松地模拟该terminate方法,并且您不必担心脚本会在您无法捕捉到它的情况下终止。

您的测试将如下所示:

class SomeClassTest extends \PHPUnit_Framework_TestCase
{

    /**
     * @expectedExceptionCode 123
     */
    public function testDoFail()
    {
        $mock = $this->getMock('SomeClass');
        $mock->expects($this->any())
             ->method('terminate')
             ->will($this->returnCallback(function($code) {
                 throw new \Exception($code);
             }));

        // run to fail
        $mock->do();
    }
}

我还没有测试过代码,但应该非常接近工作状态。

于 2014-02-05T13:08:31.193 回答
26

由于每个测试都由同一个 PHPUnit 进程运行,因此如果您在 PHP 代码中使用 exit/die,您将杀死一切——正如您所注意到的 ^^

所以,你必须找到另一个解决方案,是的——比如回归而不是死亡;或抛出异常(您可以测试某些测试代码是否抛出了预期的异常)

也许 PHPUnit 3.4 和它的--process-isolation开关(请参阅可选地使用单独的 PHP 进程执行每个测试可能会有所帮助(不会让所有东西都死掉),但是如果 PHPUnit 没有得到,您仍然无法获得测试结果控制回来。

我遇到过几次这个问题;通过返回而不是死亡来解决它 - 如果需要,甚至返回几次,以便在调用堆栈中返回“足够高”^^
最后,我想我的应用程序中不再有任何“死亡”。 .. 在考虑 MVC 时,可能会更好,顺便说一句。

于 2009-08-28T15:50:22.777 回答
14

无需更改代码即可对其进行测试,您可以简单地使用set_exit_overload()(由test_helpers与 PHPUnit 相同的作者提供)。

于 2012-02-29T11:52:00.590 回答
7

我意识到你已经接受了这个答案,这是一个老问题,但我认为这可能对某人有用,所以这里是:

除了 using die(),您还可以使用throw new RuntimeException()(或您自己的异常类),这也将停止程序执行(尽管以不同的方式)并使用 PHPUnitsetExpectedException()来捕获它。如果您希望您的脚本die()在遇到该异常时,在用户级别上完全不打印任何内容,请查看set_exception_handler().

具体来说,我正在考虑一种场景,您将set_exception_handler()-call 放入测试不使用的引导文件中,因此无论场景如何,处理程序都不会在那里触发,因此不会干扰 PHPUnit 的本机异常处理.

于 2010-04-21T13:33:04.070 回答
5

这与我一直在让一些遗留代码通过测试的一系列问题有关。所以我想出了一个像这样的可测试类......

class Testable {
   static function exitphp() {
      if (defined('UNIT_TESTING')) {
         throw new TestingPhpExitException();
      } else {
         exit();
      }
   }
}

现在我只需用 Testable::exitphp() 替换对 exit() 的调用。

如果它在测试中,我只定义 UNIT_TESTING,在生产中我没有。看起来像一个简单的模拟。

于 2013-12-12T21:11:30.593 回答
0

您可以终止脚本或引发异常,具体取决于环境变量的值...

所以你在生产环境中杀死或在测试环境中抛出异常。

任何死亡或退出的调用,都会杀死整个过程......

这应该是一个评论,但我无法评论我的声誉点水平。

于 2019-08-07T07:31:21.300 回答