2009-08-04 100 views
11

此问题特定于使用PHPUnit。测试用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); 
} 

任何想法如何,我可以做到这一点?

回答

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_NOTICEE_STRICT错误,使用PHPUnit_Framework_Error_Notice
  • 否则,使用PHPUnit_Framework_Error的异常类。

所以,是的,E_USER_*的错误不会变成PHPUnit的* _Warning或* _Notice类,它们仍然变成了一个普通的PHPUnit_Framework_Error例外。

再思考

虽然它究竟是依赖于功能如何使用,我可能会切换到抛出实际的异常,而不是触发一个错误,如果它是我的。是的,这会改变方法的逻辑流程,以及使用该方法的代码......现在,执行不能在无法读取文件时停止。但是,这取决于您决定所请求的文件是否存在真正的例外行为。我倾向于使用异常方式而不是错误/警告/通知,因为它们更易于处理,测试和工作到应用程序流程中。我通常会保留折旧方法调用等通知。

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 and 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; 
} 

所以,也许你必须通过某种参数来激活该行为?但它似乎默认启用...

+0

这不是他所问的。 – jason 2009-08-04 13:48:01

+0

杰森是正确的 - 不完全是我问的,但好的技巧通过使用PHPUnit异常类型检查特定的PHP错误类型。 – dellsala 2009-08-04 15:36:20

+0

@jason:哦,你的权利:-(抱歉:-(很好的答案,顺便说一句(有关使用两个单独的测试是一个很好的观点) @dellsala:谢谢:-) – 2009-08-04 16:32:59

8

使用phpunit.xml配置文件并禁用通知/警告/错误到异常转换。更多details in the manual。它基本上是这样的:

<phpunit convertErrorsToExceptions="false" 
     convertNoticesToExceptions="false" 
     convertWarningsToExceptions="false"> 
</phpunit> 
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'); 
} 

请注意,以测试你必须使用错误抑制操作的函数调用(在@函数名前)的返回值。这种方式不会抛出异常,执行将继续。然后您必须像往常一样设置预期的异常来测试错误。

你不能做的是在单元测试中测试多个异常。