如果该函数应该杀死 PHP,您如何使用 PHPUnit 来测试该函数?

Posted

技术标签:

【中文标题】如果该函数应该杀死 PHP,您如何使用 PHPUnit 来测试该函数?【英文标题】:How do you use PHPUnit to test a function if that function is supposed to kill PHP? 【发布时间】:2010-11-23 18:49:17 【问题描述】:

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

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

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

有没有更好的办法?

【问题讨论】:

理论上,register_shutdown_function 或许能够提供解决方案,尽管我不太确定这样做的含义。 【参考方案1】:

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

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

也许是 PHPUnit 3.4 和它的 --process-isolation 开关(参见 Optionally execute each test using a separate PHP process)可能帮助 (不会让所有东西都死掉),但你仍然无法获取测试结果,如果 PHPUnit 没有取回控制权。

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

【讨论】:

是的,现在我决定抛出一个异常。问题是我的默认异常处理程序旨在调用此函数,因此我必须创建一个名为 killProgramException 的新异常,我的异常处理程序会忽略该异常。有点老套【参考方案2】:

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

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

具体来说,我正在考虑一个场景,您将 set_exception_handler()-call 放入测试不使用的引导文件中,因此无论场景如何,处理程序都不会在那里触发,所以什么都没有干扰 PHPUnit 的原生异常处理。

【讨论】:

喜欢我的旧帖子回答了我的问题。 这个答案基本上建议使用异常作为流控制结构,这通常是不可取的。【参考方案3】:

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

【讨论】:

懒人的链接:github.com/php-test-helpers/php-test-helpers(注意这是一个 PHP 扩展,需要在 php.ini 中激活) 注意:此项目不再维护(自 2014 年起)。【参考方案4】:

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

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

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

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

【讨论】:

IMO,这非常优雅。谢谢。 这在开始时可能看起来不错,但这只是为了测试而改变程序的行为,这最终不会很好恕我直言【参考方案5】:

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

例如,不要这样:

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();
    

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

【讨论】:

【参考方案6】:

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

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

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

这应该是评论,但我无法评论我的声誉点数。

【讨论】:

以上是关于如果该函数应该杀死 PHP,您如何使用 PHPUnit 来测试该函数?的主要内容,如果未能解决你的问题,请参考以下文章

使用Java杀死进程

如何使用 php 中的 pid 杀死 linux 进程?

如何通过管道给出参数杀死[重复]

QProcess 被杀死或成功完成

QT - >如果线程正在消耗所有资源并且不允许插槽执行,如何杀死线程

Boost,C ++如何杀死另一个线程打开的线程?