【问题标题】:test the return value of a method that triggers an error with PHPUnit使用 PHPUnit 测试触发错误的方法的返回值
【发布时间】:2010-11-16 13:57:45
【问题描述】:

这个问题是针对使用 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);
}

有什么想法可以实现吗?

【问题讨论】:

    标签: php unit-testing exception phpunit


    【解决方案1】:

    这个答案对派对来说有点晚了,但无论如何:

    您可以使用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');
        }
    }
    

    希望这对将来的人有所帮助。

    【讨论】:

      【解决方案2】:

      与其期待一个通用的“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_NoticePHPUnit_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;
      }
      

      所以也许您必须传递某种参数来激活该行为?不过好像是默认开启的……

      【讨论】:

      • 这不是他要问的。
      • jason 是对的——不完全是我的要求,但是关于使用 PHPUnit 异常类型检查特定 php 错误类型的好提示。
      • @jason :哦,你的权利 :-( 抱歉 :-( 不错的答案,顺便说一句(使用两个单独的测试绝对是一个好点)@dellsala :谢谢 :-)
      【解决方案3】:

      其实有一种方法可以同时测试返回值和抛出的异常(这里是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】:

        没有办法在一个单元测试中做到这一点。如果您将测试返回值和通知分成两个不同的测试,这是可能的。

        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 异常。

        进一步的想法

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

        【讨论】:

        • 谢谢杰森。您是否碰巧知道 PHPUnit_Framework_Error_Warning::$enabled 值是否会在测试之间自动恢复?还是需要手动改回原来的值?
        • 关于这个答案的注释。虽然它适用于内置 php 函数和方法生成的错误,但它似乎不适用于用户生成的错误类型(E_USER_WARNING 和 E_USER_NOTICE)trigger_error。看起来 PHPUnit 不支持在运行时切换这些(版本 3.3.17)
        • 错误:找不到类“PHPUnit_Framework_Error_Warning”
        【解决方案5】:

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

        <phpunit convertErrorsToExceptions="false"
                 convertNoticesToExceptions="false"
                 convertWarningsToExceptions="false">
        </phpunit>
        

        【讨论】:

        • @lonut - 我在找这个! 2 年前的帖子,仍然具有相关性。
        猜你喜欢
        • 2015-02-15
        • 2012-12-18
        • 2017-09-12
        • 2015-08-23
        • 2021-02-12
        • 1970-01-01
        • 2010-11-14
        • 2013-06-20
        • 1970-01-01
        相关资源
        最近更新 更多