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


当前回答

以下是您可以执行的所有异常断言。注意,它们都是可选的。

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 5.5+上运行,你可以使用::class解析通过expectException/setExpectedException获取类名。这有几个好处:

名称将完全限定其名称空间(如果有的话)。 它将解析为一个字符串,因此它将适用于任何版本的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

into

"\My\Cool\Package\WrongPasswordException"

没有PHPUnit是明智的。

注意:PHPUnit 5.2引入了expectException作为setExpectedException的替换。

你也可以在PHPUnit 9发布之前使用文档块注释:

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

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

对于PHPUnit 5.7.27和PHP 5.6,要在一个测试中测试多个异常,强制进行异常测试是很重要的。如果没有异常发生,则单独使用异常处理来断言exception实例将跳过对情况的测试。

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

}

全面的解决方案

PHPUnit当前异常测试的“最佳实践”似乎…低迷(文档)。

由于我想要的不仅仅是当前的expectException实现,所以我在测试用例中使用了一个trait。它只有大约50行代码。

每个测试支持多个异常 支持异常抛出后调用的断言 健壮和清晰的使用示例 标准断言语法 不仅支持消息、代码和类的断言 支持反向断言,assertNotThrows 支持PHP 7可抛出错误

图书馆

我发布了AssertThrows trait到Github和packagist,这样它就可以与composer一起安装了。

简单的例子

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

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

?>

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

我使用这个helper函数来断言某个函数抛出异常:

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