2011-04-16 107 views
196

有谁知道是否有assert或类似的东西,可以测试是否在被测试的代码中抛出异常?PHPUnit断言引发了异常?

+1

为这些问题的答案:什么测试函数中的多重断言,我只希望有一个抛出异常?我是否需要将它们分开并将其置于独立的测试功能中? – 2016-01-22 08:04:53

回答

311
<?php 
require_once 'PHPUnit/Framework.php'; 

class ExceptionTest extends PHPUnit_Framework_TestCase 
{ 
    public function testException() 
    { 
     $this->expectException(InvalidArgumentException::class); 
     // or for PHPUnit < 5.2 
     // $this->setExpectedException(InvalidArgumentException::class); 

     //...and then add your test code that generates the exception 
     exampleMethod($anInvalidArgument); 
    } 
} 

expectException() PHPUnit documentation

PHPUnit author article提供测试异常的最佳做法的详细说明。

+58

+1不依赖魔术文档块 – SuperFamousGuy 2014-04-22 19:29:20

+6

如果您使用命名空间,那么您需要输入完整的命名空间:'$ this-> setExpectedException('\ My \ Name \ Space \ MyCustomException');' – Alcalyn 2014-11-25 15:50:09

+2

请注意,似乎没有一种明确期望没有例外的方法。您只需调用您的方法,如果抛出异常,测试将自动失败。 – 2015-06-23 14:53:11

91

你也可以使用一个docblock annotation

class ExceptionTest extends PHPUnit_Framework_TestCase 
{ 
    /** 
    * @expectedException InvalidArgumentException 
    */ 
    public function testException() 
    { 
     ... 
    } 
} 

对于PHP 5.5+(特别是与命名空间中的代码),我现在更喜欢用下面::class

+2

IMO,这是首选的方法。 – 2012-06-15 20:16:38

+3

@MikePurcell,为什么? – 2013-05-23 07:04:00

+0

@Falken教授:个人喜好。 Docblock注释保持测试方法的清洁,并且几个框架正在使用相同的做法; symfony 2,zf2。 – 2013-05-23 17:35:23

23

代码将测试异常消息和异常代码。

重要提示:如果预期的异常没有被抛出,它将会失败。

try{ 
    $test->methodWhichWillThrowException();//if this method not throw exception it must be fail too. 
    $this->fail("Expected exception 1162011 not thrown"); 
}catch(MySpecificException $e){ //Not catching a generic Exception or the fail function is also catched 
    $this->assertEquals(1162011, $e->getCode()); 
    $this->assertEquals("Exception Message", $e->getMessage()); 
} 
+5

'$ this-> fail()'并不意味着以这种方式使用,我不认为,至少目前不是(PHPUnit 3.6.11);它本身就是一种例外。使用你的例子,如果'$ this-> fail(“Expected exception not thrown”)被调用,那么'catch'模块被触发并且'$ e-> getMessage()'是_“期望的异常不被抛出_ 。 – ken 2014-04-16 21:33:51

+1

@ken你可能是对的。对'失败'的调用可能属于* catch块之后,而不是在try中。 – 2014-04-22 20:41:57

+1

我必须downvote,因为'fail'的调用不应该在'try'块中。它本身触发了“catch”块产生虚假结果。 – Twifty 2015-04-09 21:53:00

21

您可以使用assertException extension在一次测试执行期间声明多个异常。

方法插入到你的测试用例和使用:

public function testSomething() 
{ 
    $test = function() { 
     // some code that has to throw an exception 
    }; 
    $this->assertException($test, 'InvalidArgumentException', 100, 'expected message'); 
} 

我还做了好看的代码爱好者trait ...

+0

您正在使用哪个PHPUnit?我正在使用PHPUnit 4.7.5,并且没有定义'assertException'。我也无法在PHPUnit手册中找到它。 – physicalattraction 2015-07-22 08:34:46

+1

'asertException'方法不是原始PHPUnit的一部分。您必须手动继承'PHPUnit_Framework_TestCase'类并添加[在上面的帖子中链接的方法](https://gist.github.com/VladaHejda/8826707)。你的测试用例会继承这个继承的类。 – hejdav 2015-07-27 11:40:03

7
public function testException() { 
    try { 
     $this->methodThatThrowsException(); 
     $this->fail("Expected Exception has not been raised."); 
    } catch (Exception $ex) { 
     $this->assertEquals($ex->getMessage(), "Exception message"); 
    } 

} 
26

如果你在运行PHP 5.5+,您可以使用::class resolution来获取expectException/setExpectedException的课程名称。这提供了几个好处:

  • 该名称将完全限定其名称空间(如果有)。
  • 它解析为string,因此它可以与任何版本的PHPUnit一起使用。
  • 您在IDE中获得代码完成。
  • 如果您输错类名称,PHP编译器会发出错误。

实施例:

namespace \My\Cool\Package; 

class AuthTest extends \PHPUnit_Framework_TestCase 
{ 
    public function testLoginFailsForWrongPassword() 
    { 
     $this->expectException(WrongPasswordException::class); 
     Auth::login('Bob', 'wrong'); 
    } 
} 

PHP编译

WrongPasswordException::class 

"\My\Cool\Package\WrongPasswordException" 

而不PHPUnit的是明智的。

注意PHPUnit 5.2 introducedexpectException作为setExpectedException的替代品。

1
/** 
* @expectedException Exception 
* @expectedExceptionMessage Amount has to be bigger then 0! 
*/ 
public function testDepositNegative() 
{ 
    $this->account->deposit(-7); 
} 

要非常小心约"/**",注意双 “*”。只写“* *”(asterix)会使你的代码失败。 也请确保您使用的是phpUnit的最新版本。在phpunit的一些早期版本中,@expectedException异常不受支持。我有4.0,它不适合我,我不得不更新到5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer与作曲家更新。

6

的另一种方式可以是follwing:

$this->expectException(\Exception::class); 
$this->expectExceptionMessage('Expected Exception Message'); 

请确保您的测试类范围\化PHPUnit_Framework_TestCase

4

这里的所有异常的断言,你可以做。请注意,它们全部是可选

class ExceptionTest extends PHPUnit_Framework_TestCase 
{ 
    public function testException() 
    { 
     // make your exception assertions 
     $this->expectException(InvalidArgumentException::class); 
     // if you use namespaces: 
     // $this->expectException('\Namespace\MyExceptio‌​n'); 
     $this->expectExceptionMessage('message'); 
     $this->expectExceptionMessageRegExp('/essage$/'); 
     $this->expectExceptionCode(123); 
     // code that throws an exception 
     throw new InvalidArgumentException('message', 123); 
    } 

    public function testAnotherException() 
    { 
     // repeat as needed 
     $this->expectException(Exception::class); 
     throw new Exception('Oh no!'); 
    } 
} 

可以找到文档here

+0

这是不正确的,因为PHP停止在第一个抛出的异常。 PHPUnit检查抛出的异常是否具有正确的类型,并说“测试正常”,它甚至不知道第二个异常。 – Finesse 2017-10-27 10:19:03

+0

@Finsse FTFY ... – Potherca 2017-11-09 09:18:37

1

PHPUnit expectException方法非常不方便,因为它允许每个测试方法只测试一个异常。

我做了这个辅助函数来断言某些函数抛出一个异常:

/** 
* Asserts that the given callback throws the given exception. 
* 
* @param string $expectClass The name of the expected exception class 
* @param callable $callback A callback which should throw the exception 
*/ 
protected function assertException(string $expectClass, callable $callback) 
{ 
    try { 
     $callback(); 
    } catch (\Throwable $exception) { 
     $this->assertInstanceOf($expectClass, $exception); 
     return; 
    } 

    $this->fail('No exception was thrown'); 
} 

这种方式添加到您的测试类,并呼吁:

public function testSomething() { 
    $this->assertException(\PDOException::class, function() { 
     new \PDO('bad:param'); 
    }); 
    $this->assertException(\PDOException::class, function() { 
     new \PDO('foo:bar'); 
    }); 
}