此问题特定于使用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);
}
这是我想写的测试类型:
public function testLoadFile ()
{
$this->assertFalse(load_file('/some/non-existent/file'));
}
我遇到的问题是触发的错误导致我的单元测试失败(应该如此)。但是如果我试图捕获它,或者设置一个预期的异常,那么在触发错误之后的任何代码都不会执行,所以我无法测试该方法的返回值。
此示例不起作用:
public function testLoadFile ()
{
$this->setExpectedException('Exception');
$result = load_file('/some/non-existent/file');
// code after this point never gets executed
$this->assertFalse($result);
}
我是如何实现这一目标的?
答案 0 :(得分:21)
在一个单元测试中无法做到这一点。如果你打破测试返回值,并将通知分成两个不同的测试,这是可能的。
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);
}
这也有使您的测试更清晰,更清晰和自我记录的额外好处。
回复:评论: 这是一个很好的问题,直到我进行了几次测试之后我才知道。它看起来好像不恢复默认/原始值,至少从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;
}
回复:第二条评论:
这不完全正确。我正在查看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
异常。
进一步思考
虽然它完全取决于函数的使用方式,但我可能会转而抛出一个实际的异常,而不是触发错误,如果是我的话。是的,这将改变方法的逻辑流程,以及使用该方法的代码......现在,当它无法读取文件时,执行不会停止。但是由您来决定所请求的文件是否存在是否真的是特殊行为。我倾向于使用异常方式而不是错误/警告/通知,因为它们更容易处理,测试并适用于您的应用程序流程。我通常会保留诸如折旧方法调用之类的通知等。
答案 1 :(得分:9)
使用phpunit.xml
配置文件并禁用通知/警告/错误到异常转换。更多details in the manual。它基本上是这样的:
<phpunit convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false">
</phpunit>
答案 2 :(得分:3)
不是期望通用的“Exception
”,而是期待“PHPUnit_Framework_Error
”呢?
这样的事情可能会:
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFailingInclude()
{
include 'not_existing_file.php';
}
我认为,也可以写成:
public function testLoadFile ()
{
$this->setExpectedException('PHPUnit_Framework_Error');
$result = load_file('/some/non-existent/file');
// code after this point never gets executed
$this->assertFalse($result);
}
有关更多信息,请参阅Testing PHP Errors
特别是,它说(引用):
PHPUnit_Framework_Error_Notice
和PHPUnit_Framework_Error_Warning
代表 PHP通知和警告。
查看我系统上的/usr/share/php/PHPUnit/TextUI/TestRunner.php文件,我看到(第198行及以下):
if (!$arguments['convertNoticesToExceptions']) {
PHPUnit_Framework_Error_Notice::$enabled = FALSE;
}
if (!$arguments['convertWarningsToExceptions']) {
PHPUnit_Framework_Error_Warning::$enabled = FALSE;
}
所以也许你必须通过某种参数来激活这种行为?但它似乎默认启用...
答案 3 :(得分:1)
实际上有一种方法可以测试返回值和抛出的异常(在这种情况下是由PHPUnit转换的错误)。
您只需执行以下操作:
public function testLoadFileTriggersErrorWhenFileNotFound()
{
$this->assertFalse(@load_file('/some/non-existent/file'));
$this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
load_file('/some/non-existent/file');
}
请注意,要测试返回值,必须在函数调用上使用错误抑制运算符(函数名前的@
)。这样就不会抛出任何异常,执行将继续。然后,您必须像往常一样设置预期的异常以测试错误。
您不能做的是在单元测试中测试多个异常。
答案 4 :(得分:0)
这个答案对聚会来说有点晚了,但是无论如何:
您可以使用Netsilik/BaseTestCase(MIT许可证)直接测试触发的通知/警告,而无需忽略它们或将其转换为异常。由于通知/警告不会转换为异常,因此不会暂停执行。
composer require netsilik/base-test-case
测试E_USER_NOTICE
:
<?php
namespace Tests;
class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
public function test_whenNoticeTriggered_weCanTestForIt()
{
$foo = new Foo();
$foo->bar();
self::assertErrorTriggered(E_USER_NOTICE, 'The notice message');
}
}
希望这对以后的人有帮助。