PHPUnit은 예외가 발생했다고 주장합니까?


337

assert테스트중인 코드에서 예외가 발생했는지 테스트 할 수있는 것과 같은 것이 있는지 아는 사람이 있습니까?


2
그 대답에 : 테스트 함수의 다중 어설 션은 어떻습니까? 그것들을 분리하고 독립적 인 테스트 기능에 넣어야합니까?
Panwen Wang

답변:


549
<?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 문서

PHPUnit 작성자 기사 는 예외 모범 사례 테스트에 대한 자세한 설명을 제공합니다.


8
네임 스페이스를 사용하는 경우 전체 네임 스페이스를 입력해야합니다.$this->setExpectedException('\My\Name\Space\MyCustomException');
Alcalyn

15
던질 것으로 예상되는 정확한 코드 줄을 지정할 수 없다는 사실은 오류 IMO입니다. 또한 동일한 테스트에서 둘 이상의 예외를 테스트 할 수 없기 때문에 예상되는 많은 예외에 대한 테스트가 정말 어려워집니다. 나는 그 문제를 해결하기 위해 실제 주장 을 썼다 .
mindplay.dk

18
참고 : phpunit 5.2.0부터 setExpectedException 메소드가 더 이상 사용되지 않고 메소드로 대체되었습니다 expectException. :)
hejdav 2016 년

41
무엇이 문서 또는 여기에 언급되지 않은,하지만 코드를 호출 할 수있는 예외 요구 던질 것으로 예상 후를 expectException() . 어떤 사람들에게는 분명했을지 모르지만 그것은 나에게 큰 어려움 이었습니다.
Jason McCreary

7
문서에서 분명하지는 않지만 예외를 발생시키는 함수 이후의 코드는 실행되지 않습니다. 따라서 동일한 테스트 사례에서 여러 예외를 테스트하려는 경우 할 수 없습니다.
laurent

122

PHPUnit 9가 출시 될 때까지 docblock 주석을 사용할 수도 있습니다 :

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

PHP 5.5 이상 (특히 네임 스페이스 코드)의 경우 이제는 ::class


3
IMO는 이것이 선호되는 방법입니다.
Mike Purcell 2016 년

12
@LeviMorrison-IMHO 예외 메시지 는 로그 메시지와 유사하게 테스트되지 않아야합니다. 둘 다 수동 법의학을 수행 할 때 관계없는 유용한 정보로 간주됩니다 . 테스트의 요점 은 예외 유형 입니다. 그 이상은 구현에 너무 밀접하게 바인딩되어 있습니다. IncorrectPasswordException메시지가 동일하다는 "Wrong password for bob@me.com"것은 부수적입니다. 테스트 작성에 적은 시간을 투자하고 간단한 테스트가 얼마나 중요한지 알아보기 시작하십시오.
David Harkness

5
@DavidHarkness 누군가 누군가 그것을 가져올 것이라고 생각했습니다. 마찬가지로 메시지 테스트는 일반적으로 너무 엄격하고 엄격하다는 데 동의합니다. 그러나 사양의 시행과 같은 일부 상황에서 원하는 경우 (고의적으로 강조 될 수 있는) 엄격하고 엄격한 바인딩입니다 .
Levi Morrison

1
나는 doc-block에서 예상되는 것을 이해하지는 않지만 실제 테스트 코드 (테스트 종류에 관계없이)를 살펴볼 것입니다. 이것이 다른 모든 테스트의 표준입니다. 나는 예외가이 협약에 대한 예외가 되는 유효한 이유를 보지 못했다 .
Kamafeather

3
"메시지 테스트 안 함"규칙은 메시지의 여러 부분에서 동일한 예외 유형을 발생시키는 메소드를 테스트하지 않는 한, 메시지에서 전달되는 오류 ID 만 다릅니다. 시스템은 예외 메시지 (예외 유형이 아님)를 기반으로 사용자에게 메시지를 표시 할 수 있습니다. 이 경우 사용자에게 표시되는 메시지가 중요하므로 오류 메시지를 테스트해야합니다.
Vanja D.

34

당신이 PHP 5.5 이상에서 실행하는 경우 사용할 수있는 ::class해상도를 가지는 클래스의 이름을 얻기 위해 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는를 expectException 대신하여 도입 되었습니다 setExpectedException.


32

아래 코드는 예외 메시지와 예외 코드를 테스트합니다.

중요 : 예상 예외도 발생하지 않으면 실패합니다.

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());
}

6
$this->fail()적어도 현재는 생각하지 않는 방식으로 사용되지는 않습니다 (PHPUnit 3.6.11). 그것은 예외 그 자체로 작용합니다. 귀하의 예제를 사용하는 경우 $this->fail("Expected exception not thrown")라고하며, 다음 catch블록은 트리거되고 $e->getMessage()있다 "예외가 발생하지 예상" .
ken

1
@ken 아마 당신이 맞을 것입니다. 호출은 fail아마도 try 내부가 아닌 catch 블록 뒤에 속합니다 .
Frank Farmer

1
에 대한 호출 failtry차단 되어서는 안되므로 하향 투표를 해야합니다 . 자체적으로 catch블록이 잘못된 결과를 생성 하도록 트리거합니다 .
Twifty

6
이것이 제대로 작동하지 않는 이유는 일부 상황에서로 모든 예외를 잡기 때문이라고 생각합니다 catch(Exception $e). 이 방법은 특정 예외를 잡으려고 할 때 매우 효과적입니다.try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
spyle

23

assertException 확장을 사용할 수 있습니다 을 한 번의 테스트 실행 중에 둘 이상의 예외를 선언 .

TestCase에 메소드를 삽입하고 다음을 사용하십시오.

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

나는 또한 좋은 코드를 좋아하는 사람들을 위해 특성 을 만들었다 ..


어떤 PHPUnit을 사용하고 있습니까? PHPUnit 4.7.5를 사용하고 있으며 assertException정의되어 있지 않습니다. 또한 PHPUnit 매뉴얼에서 찾을 수 없습니다.
신체적

2
asertException메소드는 원래 PHPUnit의 일부가 아닙니다. 위의 게시물에 링크 된PHPUnit_Framework_TestCase 클래스 를 상속하고 메소드를 수동으로 추가 해야합니다 . 그런 다음 테스트 사례는이 상속 된 클래스를 상속합니다.
hejdav

18

다른 방법은 다음과 같습니다.

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

당신의 테스트 클래스 범위를 확인하십시오 \PHPUnit_Framework_TestCase.


이 구문에서 확인 대부분의 설탕
AndrewMcLagan

13

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, 'An invalid exception was thrown');
        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');
    });
}

모든 답변 중에서 확실히 최고의 솔루션! 그것을 한 특성에 버리고 포장하십시오!
domdambrogia

11

종합적인 솔루션

예외 테스트를위한 PHPUnit의 현재 " 모범 사례 "는 .. lackluster ( docs )입니다.

현재 구현 보다 더 많은 것을 원했기 때문에 expectException테스트 사례에 사용할 특성을 만들었습니다. 그것은 단지의 코드 ~ 50 라인 .

  • 테스트 당 여러 예외 지원
  • 예외가 발생한 후 호출 된 어설 션 지원
  • 강력하고 명확한 사용 예
  • 표준 assert구문
  • 메시지, 코드 및 클래스 이상의 어설 션 지원
  • 역 주장 지원 assertNotThrows
  • PHP 7 Throwable오류 지원

도서관

나는 AssertThrows특성을 Github과 packagist에 게시하여 작곡가와 함께 설치할 수 있습니다.

간단한 예

구문의 기본 개념을 설명하기 위해 :

<?php

// Using simple callback
$this->assertThrows(MyException::class, [$obj, 'doSomethingBad']);

// Using anonymous function
$this->assertThrows(MyException::class, function() use ($obj) {
    $obj->doSomethingBad();
});

꽤 깔끔한?


전체 사용 예

보다 포괄적 인 사용 예는 아래를 참조하십시오.

<?php

declare(strict_types=1);

use Jchook\AssertThrows\AssertThrows;
use PHPUnit\Framework\TestCase;

// These are just for illustration
use MyNamespace\MyException;
use MyNamespace\MyObject;

final class MyTest extends TestCase
{
    use AssertThrows; // <--- adds the assertThrows method

    public function testMyObject()
    {
        $obj = new MyObject();

        // Test a basic exception is thrown
        $this->assertThrows(MyException::class, function() use ($obj) {
            $obj->doSomethingBad();
        });

        // Test custom aspects of a custom extension class
        $this->assertThrows(MyException::class, 
            function() use ($obj) {
                $obj->doSomethingBad();
            },
            function($exception) {
                $this->assertEquals('Expected value', $exception->getCustomThing());
                $this->assertEquals(123, $exception->getCode());
            }
        );

        // Test that a specific exception is NOT thrown
        $this->assertNotThrows(MyException::class, function() use ($obj) {
            $obj->doSomethingGood();
        });
    }
}

?>

4
단위 테스트 용 패키지에 저장소의 단위 테스트가 포함되어 있지 않다는 약간의 아이러니.
domdambrogia

2
@ jean- beguin 덕분에 @domdambrogia 는 이제 단위 테스트를했습니다.
jchook

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

}

의 서명이 assertEquals()되어 assertEquals(mixed $expected, mixed $actual...)그것이 있어야하므로, 귀하의 예에서와 같이 역$this->assertEquals("Exception message", $ex->getMessage());
로저 Campanera에게

7

여기 당신이 할 수있는 모든 예외 주장이 있습니다. 그들 모두는 선택 사항 입니다.

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!');
    }
}

설명서는 여기 에서 찾을 수 있습니다 .


PHP가 첫 번째 예외에서 중지하기 때문에 올바르지 않습니다. PHPUnit은 발생 된 예외가 올바른 유형인지 확인하고«테스트가 정상입니다»라고 말하며, 두 번째 예외에 대해서도 알지 못합니다.
Finesse

3
/**
 * @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 로 업데이트해야했다.


0

PHPUnit 5.7.27 및 PHP 5.6의 경우 한 번의 테스트로 여러 예외를 테스트하려면 예외 테스트를 강제 실행하는 것이 중요했습니다. 예외 처리 만 단독으로 사용하면 예외가 발생하지 않으면 예외 인스턴스가 상황 테스트를 건너 뜁니다.

public function testSomeFunction() {

    $e=null;
    $targetClassObj= new TargetClass();
    try {
        $targetClassObj->doSomething();
    } catch ( \Exception $e ) {
    }
    $this->assertInstanceOf(\Exception::class,$e);
    $this->assertEquals('Some message',$e->getMessage());

    $e=null;
    try {
        $targetClassObj->doSomethingElse();
    } catch ( Exception $e ) {
    }
    $this->assertInstanceOf(\Exception::class,$e);
    $this->assertEquals('Another message',$e->getMessage());

}

0
function yourfunction($a,$z){
   if($a<$z){ throw new <YOUR_EXCEPTION>; }
}

여기 시험이있다

class FunctionTest extends \PHPUnit_Framework_TestCase{

   public function testException(){

      $this->setExpectedException(<YOUR_EXCEPTION>::class);
      yourfunction(1,2);//add vars that cause the exception 

   }

}

0

PhpUnit은 놀라운 라이브러리이지만이 점은 약간 실망 스럽습니다. 그렇기 때문에 예외 테스트에 도움이되는 매우 편리한 어설 션 방법이있는 turbotesting-php 오픈 소스 라이브러리를 사용할 수 있습니다. 여기에 있습니다.

https://github.com/edertone/TurboTesting/blob/master/TurboTesting-Php/src/main/php/utils/AssertUtils.php

그리고 그것을 사용하기 위해 우리는 단순히 다음을 수행합니다.

AssertUtils::throwsException(function(){

    // Some code that must throw an exception here

}, '/expected error message/');

익명 함수에 입력 한 코드에서 예외가 발생하지 않으면 예외가 발생합니다.

익명 함수에 입력 한 코드에서 예외가 발생하지만 해당 메시지가 예상 정규 표현식과 일치하지 않으면 예외도 발생합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.