PHPUnit断言抛出异常?

时间:2022-02-27 20:30:22

Does anyone know whether there is an assert or something like that which can test whether an exception was thrown in the code being tested?

有人知道是否有断言或类似的东西可以测试被测试的代码中是否抛出了异常吗?

11 个解决方案

#1


343  

<?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

expectException()PHPUnit)文档

PHPUnit author article provides detailed explanation on testing exceptions best practices.

PHPUnit作者的文章详细解释了异常测试的最佳实践。

#2


96  

You can also use a docblock annotation:

您还可以使用docblock注释:

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

For PHP 5.5+ (especially with namespaced code), I now prefer using ::class

对于PHP 5.5+(特别是有名称空间的代码),我现在更喜欢使用::class

#3


28  

If you're running on PHP 5.5+, you can use ::class resolution to obtain the name of the class with expectException/setExpectedException. This provides several benefits:

如果您运行的是PHP 5.5+,那么可以使用::类解析来获取具有expectException/setExpectedException的类的名称。这提供了一些好处:

  • The name will be fully-qualified with its namespace (if any).
  • 名称将完全限定其命名空间(如果有的话)。
  • It resolves to a string so it will work with any version of PHPUnit.
  • 它解析为一个字符串,因此可以使用任何版本的PHPUnit。
  • You get code-completion in your IDE.
  • 您将在IDE中获得代码完成。
  • The PHP compiler will emit an error if you mistype the class name.
  • 如果错误地编写类名,PHP编译器将产生错误。

Example:

例子:

namespace \My\Cool\Package;

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

PHP compiles

PHP编译

WrongPasswordException::class

into

"\My\Cool\Package\WrongPasswordException"

without PHPUnit being the wiser.

没有PHPUnit更聪明。

Note: PHPUnit 5.2 introduced expectException as a replacement for setExpectedException.

注:PHPUnit 5.2引入了预期拦截作为setExpectedException的替代。

#4


23  

Code below will test exception message and exception code.

下面的代码将测试异常消息和异常代码。

Important: It will fail if expected exception not thrown too.

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

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


22  

You can use assertException extension to assert more than one exception during one test execution.

您可以使用assertException扩展在一次测试执行期间断言多个异常。

Insert method into your TestCase and use:

将方法插入到您的测试用例中并使用:

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

I also made a trait for lovers of nice code..

我也为喜欢漂亮代码的人创造了一个特点。

#6


7  

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

}

#7


6  

An alternative way can be the follwing:

另一种方法可能是:

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

Please ensure that your test class extents \PHPUnit_Framework_TestCase

请确保您的测试类扩展\PHPUnit_Framework_TestCase

#8


4  

Here's all the exception assertions you can do. Note that all of them are optional.

这里是您可以做的所有异常断言。注意,所有这些都是可选的。

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

Documentation can be found here.

文档可以在这里找到。

#9


3  

PHPUnit's current "best practices" for exception testing are.. lackluster.

PHPUnit当前用于异常测试的“最佳实践”是……乏善可陈。

  • Doesn't support multiple exceptions per test, or assertions called after the exception is thrown
  • 不支持每个测试的多个异常,或抛出异常后的断言。
  • Documentation lacks robust or clear examples
  • 文档缺乏健壮的或清晰的示例
  • Non-standard and potentially confusing syntax ("expect" vs "assert")
  • 非标准的、可能会混淆的语法(“expect”与“assert”)
  • Only supports assertions for message, code, and class
  • 只支持消息、代码和类的断言
  • No inverse, such as "expectNoException"
  • 没有反义词,比如“expectNoException”

I opened a Github issue for PHPUnit and was immediately dismissed by the maintainer.

我为PHPUnit打开了一个Github的问题,立即被维护人员解雇。

Since I strongly disagree with the current expectException implementation, I made a trait to use on my test cases.

由于我强烈反对当前的预期实现,所以我在测试用例中使用了一个trait。

Library

The AssertThrows trait is published to Github and packagist so it can be installed with composer.

assertthrow特性被发布给Github和packagist,这样它就可以与composer一起安装了。

Simple Example

Just to illustrate the spirit behind the syntax:

只是为了说明语法背后的精神:

<?php

// Within your test case...
$this->assertThrows(MyException::class, function() use ($obj) {
    $obj->doSomethingBad();
});

Pretty neat?

很整洁的?


Full Usage Example

Here is an actual TestCase class that shows a more comprehensive usage example:

下面是一个实际的TestCase类,它显示了一个更全面的使用示例:

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

?>

#10


2  

The PHPUnit expectException method is very inconvenient because it allows to test only one exception per a test method.

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

I've made this helper function to assert that some function throws an exception:

我让这个助手函数断言某个函数抛出一个异常:

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

Add it to your test class and call this way:

将它添加到您的测试类中,并这样调用:

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

#11


1  

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

Be very carefull about "/**", notice the double "*". Writing only "**"(asterix) will fail your code. Also make sure your using last version of phpUnit. In some earlier versions of phpunit @expectedException Exception is not supported. I had 4.0 and it didn't work for me, I had to update to 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer to update with composer.

小心“/**”,注意双“*”。只编写“**”(asterix)将导致代码失败。还要确保使用最后一个phpUnit版本。在phpunit @expectedException的早期版本中,不支持这个异常。我有4.0,但它对我没有效果,我必须更新到5.5 https://coderwall.com/p/mklvdw/installphwith- composer,以更新作曲家。

#1


343  

<?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

expectException()PHPUnit)文档

PHPUnit author article provides detailed explanation on testing exceptions best practices.

PHPUnit作者的文章详细解释了异常测试的最佳实践。

#2


96  

You can also use a docblock annotation:

您还可以使用docblock注释:

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

For PHP 5.5+ (especially with namespaced code), I now prefer using ::class

对于PHP 5.5+(特别是有名称空间的代码),我现在更喜欢使用::class

#3


28  

If you're running on PHP 5.5+, you can use ::class resolution to obtain the name of the class with expectException/setExpectedException. This provides several benefits:

如果您运行的是PHP 5.5+,那么可以使用::类解析来获取具有expectException/setExpectedException的类的名称。这提供了一些好处:

  • The name will be fully-qualified with its namespace (if any).
  • 名称将完全限定其命名空间(如果有的话)。
  • It resolves to a string so it will work with any version of PHPUnit.
  • 它解析为一个字符串,因此可以使用任何版本的PHPUnit。
  • You get code-completion in your IDE.
  • 您将在IDE中获得代码完成。
  • The PHP compiler will emit an error if you mistype the class name.
  • 如果错误地编写类名,PHP编译器将产生错误。

Example:

例子:

namespace \My\Cool\Package;

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

PHP compiles

PHP编译

WrongPasswordException::class

into

"\My\Cool\Package\WrongPasswordException"

without PHPUnit being the wiser.

没有PHPUnit更聪明。

Note: PHPUnit 5.2 introduced expectException as a replacement for setExpectedException.

注:PHPUnit 5.2引入了预期拦截作为setExpectedException的替代。

#4


23  

Code below will test exception message and exception code.

下面的代码将测试异常消息和异常代码。

Important: It will fail if expected exception not thrown too.

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

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


22  

You can use assertException extension to assert more than one exception during one test execution.

您可以使用assertException扩展在一次测试执行期间断言多个异常。

Insert method into your TestCase and use:

将方法插入到您的测试用例中并使用:

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

I also made a trait for lovers of nice code..

我也为喜欢漂亮代码的人创造了一个特点。

#6


7  

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

}

#7


6  

An alternative way can be the follwing:

另一种方法可能是:

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

Please ensure that your test class extents \PHPUnit_Framework_TestCase

请确保您的测试类扩展\PHPUnit_Framework_TestCase

#8


4  

Here's all the exception assertions you can do. Note that all of them are optional.

这里是您可以做的所有异常断言。注意,所有这些都是可选的。

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

Documentation can be found here.

文档可以在这里找到。

#9


3  

PHPUnit's current "best practices" for exception testing are.. lackluster.

PHPUnit当前用于异常测试的“最佳实践”是……乏善可陈。

  • Doesn't support multiple exceptions per test, or assertions called after the exception is thrown
  • 不支持每个测试的多个异常,或抛出异常后的断言。
  • Documentation lacks robust or clear examples
  • 文档缺乏健壮的或清晰的示例
  • Non-standard and potentially confusing syntax ("expect" vs "assert")
  • 非标准的、可能会混淆的语法(“expect”与“assert”)
  • Only supports assertions for message, code, and class
  • 只支持消息、代码和类的断言
  • No inverse, such as "expectNoException"
  • 没有反义词,比如“expectNoException”

I opened a Github issue for PHPUnit and was immediately dismissed by the maintainer.

我为PHPUnit打开了一个Github的问题,立即被维护人员解雇。

Since I strongly disagree with the current expectException implementation, I made a trait to use on my test cases.

由于我强烈反对当前的预期实现,所以我在测试用例中使用了一个trait。

Library

The AssertThrows trait is published to Github and packagist so it can be installed with composer.

assertthrow特性被发布给Github和packagist,这样它就可以与composer一起安装了。

Simple Example

Just to illustrate the spirit behind the syntax:

只是为了说明语法背后的精神:

<?php

// Within your test case...
$this->assertThrows(MyException::class, function() use ($obj) {
    $obj->doSomethingBad();
});

Pretty neat?

很整洁的?


Full Usage Example

Here is an actual TestCase class that shows a more comprehensive usage example:

下面是一个实际的TestCase类,它显示了一个更全面的使用示例:

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

?>

#10


2  

The PHPUnit expectException method is very inconvenient because it allows to test only one exception per a test method.

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

I've made this helper function to assert that some function throws an exception:

我让这个助手函数断言某个函数抛出一个异常:

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

Add it to your test class and call this way:

将它添加到您的测试类中,并这样调用:

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

#11


1  

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

Be very carefull about "/**", notice the double "*". Writing only "**"(asterix) will fail your code. Also make sure your using last version of phpUnit. In some earlier versions of phpunit @expectedException Exception is not supported. I had 4.0 and it didn't work for me, I had to update to 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer to update with composer.

小心“/**”,注意双“*”。只编写“**”(asterix)将导致代码失败。还要确保使用最后一个phpUnit版本。在phpunit @expectedException的早期版本中,不支持这个异常。我有4.0,但它对我没有效果,我必须更新到5.5 https://coderwall.com/p/mklvdw/installphwith- composer,以更新作曲家。