测试使用PHPUnit触发错误的方法的返回值

del*_*ala 12 php phpunit unit-testing exception

这个问题特定于使用PHPUnit.

PHPUnit自动将php错误转换为异常.有没有办法测试碰巧触发php错误的方法的返回值(内置错误或用户生成的错误通过trigger_error)?

要测试的代码示例:

function load_file ($file)
{
    if (! file_exists($file)) {
        trigger_error("file {$file} does not exist", E_USER_WARNING);
        return false;
    }
    return file_get_contents($file);
}
Run Code Online (Sandbox Code Playgroud)

这是我想写的测试类型:

public function testLoadFile ()
{
    $this->assertFalse(load_file('/some/non-existent/file'));
}
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是触发的错误导致我的单元测试失败(应该如此).但是如果我试图捕获它,或者设置一个预期的异常,那么在触发错误之后的任何代码都不会执行,所以我无法测试该方法的返回值.

此示例不起作用:

public function testLoadFile ()
{
    $this->setExpectedException('Exception');
    $result = load_file('/some/non-existent/file');

    // code after this point never gets executed

    $this->assertFalse($result);
}
Run Code Online (Sandbox Code Playgroud)

有什么想法我能做到这一点吗?

jas*_*son 22

在一个单元测试中无法做到这一点.如果你打破测试返回值,并将通知分成两个不同的测试,这是可能的.

PHPUnit的错误处理程序捕获PHP错误和通知并将它们转换为异常 - 根据定义,它会停止程序执行.您正在测试的功能永远不会返回.但是,您可以暂时禁用将错误转换为异常,即使在运行时也是如此.

通过一个例子,这可能更容易,因此,这两个测试应该是这样的:

public function testLoadFileTriggersErrorWhenFileNotFound()
{
    $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
    $result = load_file('/some/non-existent/file');

}

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    PHPUnit_Framework_Error_Warning::$enabled = FALSE;
    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);
}
Run Code Online (Sandbox Code Playgroud)

这也有使您的测试更清晰,更清晰和自我记录的额外好处.

回复:评论: 这是一个很好的问题,直到我进行了几次测试后才知道.它看起来好像不会恢复默认/原始值,至少从PHPUnit 3.3.17(现在的当前稳定版本)开始.

所以,我实际上会修改上面的内容,如下所示:

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    $warningEnabledOrig = PHPUnit_Framework_Error_Warning::$enabled;
    PHPUnit_Framework_Error_Warning::$enabled = false;

    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);

    PHPUnit_Framework_Error_Warning::$enabled = $warningEnabledOrig;
}
Run Code Online (Sandbox Code Playgroud)

回复:第二评论:

这不完全正确.我正在看PHPUnit的错误处理程序,它的工作原理如下:

  • 如果是E_WARNING,则PHPUnit_Framework_Error_Warning用作异常类.
  • 如果是E_NOTICE或E_STRICT错误,请使用PHPUnit_Framework_Error_Notice
  • 否则,PHPUnit_Framework_Error用作异常类.

所以,是的,它的错误E_USER_*没有变成PHPUnit的*_Warning或*_Notice类,它们仍然被转换为通用PHPUnit_Framework_Error异常.

进一步思考

虽然它完全取决于函数的使用方式,但我可能会切换到抛出实际异常而不是触发错误,如果是我的话.是的,这将改变方法的逻辑流程,以及使用该方法的代码......现在,当它无法读取文件时,执行不会停止.但是由您决定所请求的文件是否存在是否真的是异常行为.我倾向于使用异常方式而不是错误/警告/通知,因为它们更容易处理,测试并适用于您的应用程序流程.我通常会保留有关折旧方法调用等的通知.


Ion*_*tan 9

使用phpunit.xml配置文件并禁用通知/警告/错误到异常转换.手册中的更多细节.它基本上是这样的:

<phpunit convertErrorsToExceptions="false"
         convertNoticesToExceptions="false"
         convertWarningsToExceptions="false">
</phpunit>
Run Code Online (Sandbox Code Playgroud)