2011-04-16 79 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'); 
    }); 
}