【问题标题】:Testing exceptions in PHPunit always fails在 PHPunit 中测试异常总是失败
【发布时间】:2012-10-23 07:10:07
【问题描述】:

我在 PHP 中几乎没有自定义异常:

class MainException extends Exception {};
class ExceptionOne extends MainException {};
class ExceptionTwo extends MainException {};

我在课堂上通过两种简单的方法使用它们:

public function firstFunction($param) {
    if ($some_condition) {
        // do whatever
    } else {
        throw new ExceptionOne();
    }
}

public function secondFunction($param) {
    if ($some_condition) {
        // do whatever
    } else {
        throw new ExceptionTwo();
    }
}

我也对这两个异常进行了 PHPUnit 测试,类似于:

public function testFirstException() {
    try {
        // anything
    } catch (Exception $e) {
        $this->assertType('ExceptionOne', $e);
        $this->assertType('MainException', $e);
    }
}
public function testSecondException() {
    try {
        // anything
    } catch (Exception $e) {
        $this->assertType('ExceptionTwo', $e);
        $this->assertType('MainException', $e);
    }
}

如果我在浏览器中测试我的类并故意让我的函数失败(使用与 PHPUnit 测试中相同的东西),我可以看到 ExceptionOne 和 ExceptionTwo 在需要时出现。但是,当我使用 PHPUnit 对其进行测试时,我总是会失败:

1) testSecondException(SomeTest)
Failed asserting that <PHPUnit_Framework_ExpectationFailedException> is an instance of class "ExceptionTwo".
C:\test.php:67

第 67 行是
$this-&gt;assertType('ExceptionTwo', $e);
无论我尝试什么,它都会失败。我很确定我在secondFunction 中的情况是正确的。第一个测试 (testFirstException) 完美运行,我从来没有像第二个测试那样失败。
我想补充一点,我不应该在这里更改 PHPUnit 测试!

我在这里做错了什么??

【问题讨论】:

    标签: php exception phpunit


    【解决方案1】:

    使用 php 单元,您必须生成关于 Try {} 中的任何方法的异常,以伪造您的响应。

    public function testFirstFunction() {
    ....
    $this->methodOnGenerateExeption(Argumen::Any())->willReturn()->willThrow(\Exception::class);
    $this->setExpectedException(\Exception::class);
    
      $this->classMockToTest->firstFunction($parameters)
    }
    

    【讨论】:

      【解决方案2】:

      从您的 cmets 到 cbuckley 的出色答案,测试似乎是这样编写的:

      public function testFirstException() {
          try {
              // some code that is supposed to throw ExceptionOne
              $this->assertTrue(false, "Test failed");
          } catch (Exception $e) {
              $this->assertType('ExceptionOne', $e);
              $this->assertType('MainException', $e);
          }
      }
      

      assertTrue 用于确保在测试期间未抛出异常的情况下测试失败。但是,这不起作用,因为失败的断言会引发不同的异常类型,从而导致错误消息令人困惑。

      Failed asserting that <PHPUnit_Framework_ExpectationFailedException> 
      is an instance of class "ExceptionOne".
      

      您可以使用@expectedException 或setExpectedException 解决此问题。不仅当ExceptionOne被抛出时测试会通过,而且当它没有被抛出或者其他一些异常类型时它会失败。

      /**
       * @expectedException ExceptionOne
       */
      public function testFirstException() {
          // some code that is supposed to throw ExceptionOne
      }
      

      当没有抛出异常时,错误信息是

      Failed asserting that exception of type "ExceptionOne" is thrown.
      

      当抛出不同类型的异常时,你会看到

      Failed asserting that exception of type "Exception" matches
      expected exception "RuntimeException".
      

      这种测试异常的方法是

      • 更容易编写,
      • 更易于阅读,
      • 测试失败时更容易调试,
      • 并且正确。

      【讨论】:

      • 是的,这就是我的try 块在测试中的编写方式。现在我不再确定自己了,我会再次检查我的课程......
      • GRRRR!!!我的正则表达式模式中的一个愚蠢的错字一直是造成这种情况的原因>。-(我现在真的觉得自己像个菜鸟:)无论如何,再次感谢所有关于测试异常的重要信息,请记住,在未来当然!
      • 我强烈建议您与编写该测试的人分享此建议。你本可以从所有这些痛苦中解救出来。
      • +1 让我的答案更清晰 :-) FWIW,我不建议使用 @expectedException,因为它只断言在 somehwere 中抛出了这样的异常测试代码。如果使用setExpectedException,您可以通过将异常放在预期失败的行之前来精确控制预期何时引发异常。
      • 呵呵,大卫,我当然会这样做:) 不幸的是最后是我的代码中的错误,即使我对PHPUnit没有太多经验,我也清楚地理解了这一点在这里... 正如 Bender 所说:学习很有趣! :)
      【解决方案3】:

      您似乎在 try/catch 中掩盖了另一个错误。如果您的 try 块中有测试断言,并且该断言失败,则将其作为异常抛出。尝试使用 setExpectedException 而不是 try/catch 块:

      public function testFirstException() {
          $this->setExpectedException('ExceptionOne');
          $fixture->firstFunction();
      }
      

      您应该将setExpectedException 调用放在您希望引发异常的行之前。

      另一种选择是使用@expectedException docblock 注释。

      /**
       * @expectedException ExceptionOne
       */
      public function testFirstException() {
          $fixture->firstFunction();
      }
      

      如果您想保留 try/catch 方法,同样适用:确保 try 块中的唯一行是应该引发异常的行。

      【讨论】:

      • 此外,如果您使用 try/catch 方法并使用 PHPUnit 3.6 或更高版本,则应使用 assertInstanceOf 而不是 assertType。这是根据 phpunit 文档本身,因为 assertType 已被弃用。
      • 正如我所说,我真的不应该在这里更改测试文件...但是我会尝试这样做,看看会发生什么...
      • 嗯...所以,在测试函数中的两个try 块中都有两行:一是从我的类中调用方法,另一行是$this-&gt;assertTrue(false, "Some message");。如果我从testSecondException 中删除第二个,一切都会按预期进行……不过,我认为我真的不应该出于几个不同的原因更改测试文件中的代码……还有什么我可以尝试的吗?为什么testFirstException 与try 块中的两行代码一起工作得很好? ://
      • 您需要修复您的测试。失败的断言通过抛出异常来标记。在这种情况下,您不应该捕获通用异常。你怎么不想改变你的测试? try/catch 方法给你带来了问题。至少,更新为像PHPunit example(即不要捕获一般异常,不要做assertTrue(false))!
      • 我明白了...嗯,非常感谢您的帮助!关于更改测试本身...我有一个任务来编写该类并使用已经为我编写的 PHPUnit 对其进行测试...任务描述中没有任何地方说“测试代码有问题”所以我想我不应该碰它:)
      猜你喜欢
      • 1970-01-01
      • 2015-08-04
      • 2014-07-07
      • 2015-12-27
      • 1970-01-01
      • 2013-07-28
      • 1970-01-01
      • 2012-03-27
      相关资源
      最近更新 更多