PHPUnit断言抛出了异常?

时间:2011-04-16 00:23:39

标签: php exception phpunit assert

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

15 个答案:

答案 0 :(得分:455)

<?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提供了有关测试例外最佳做法的详细说明。

答案 1 :(得分:112)

您还可以使用docblock annotation

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

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

答案 2 :(得分:33)

如果您在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 introduced expectException替代setExpectedException

答案 3 :(得分:27)

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

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

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

答案 4 :(得分:23)

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

将方法插入TestCase并使用:

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

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

答案 5 :(得分:11)

另一种方法可以是:

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

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

答案 6 :(得分:9)

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

}

答案 7 :(得分:7)

综合解决方案

PHPUnit&#39; best practices&#34;异常测试似乎..乏力(docs)。

由于我wanted more比当前expectException实现,我在我的测试用例中使用了一个特性。它只是~50 lines of code

  • 每次测试支持多个例外
  • 支持抛出异常后调用的断言
  • 强大而清晰的用法示例
  • 标准assert语法
  • 支持断言不仅仅是消息,代码和类
  • 支持反向断言assertNotThrows
  • 支持PHP 7 Throwable错误

我向Github和AssertThrows发布了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();
        });
    }
}

?>

答案 8 :(得分:6)

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

答案 9 :(得分: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

答案 10 :(得分:2)

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

要非常小心"/**",请注意双重&#34; *&#34;。仅写入&#34; **&#34;(asterix)将使您的代码失败。 还要确保使用最新版本的phpUnit。在某些早期版本的phpunit @expectedException中,不支持异常。我有4.0并且它对我不起作用,我必须更新到5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer以更新作曲家。

答案 11 :(得分:2)

TLDR;滚动到:使用 PHPUnit 的数据提供程序

PHPUnit 9.5 提供了以下方法来测试异常:

$this->expectException(string $exceptionClassName);
$this->expectExceptionCode(int|string $code);
$this->expectExceptionMessage(string $message);
$this->expectExceptionMessageMatches(string $regularExpression);
$this->expectExceptionObject(\Exception $exceptionObject);

然而,Documentation 对测试代码中上述任何方法的顺序含糊不清。

如果您习惯使用断言,例如:

<?php

class SimpleAssertionTest extends \PHPUnit\Framework\TestCase
{
    public function testSimpleAssertion(): void
    {
        $expected = 'bar';
        $actual = 'bar';
        $this->assertSame($expected, $actual);
    }
}

输出:

 ✔ Simple assertion
OK (1 test, 1 assertion)

您可能会对未通过异常测试感到惊讶:

<?php

use PHPUnit\Framework\TestCase;

final class ExceptionTest extends TestCase
{
    public function testException(): void
    {
        throw new \InvalidArgumentException();
        $this->expectException(\InvalidArgumentException::class);
    }
}

输出:

 ✘ Exception
   ├ InvalidArgumentException:

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

错误是因为:

<块引用>

一旦抛出异常,PHP 将无法返回到抛出异常行之后的代码行。捕获异常在这方面没有任何改变。抛出异常是单程票。

与错误不同,异常没有能力从它们中恢复并使 PHP 继续执行代码,就好像根本没有异常一样。

因此 PHPUnit 甚至没有到达那个地方:

$this->expectException(\InvalidArgumentException::class);

如果前面是:

throw new \InvalidArgumentException();

此外,无论 PHPUnit 的异常捕获能力如何,它都永远无法到达那个地方。

因此使用任何 PHPUnit 的异常测试方法:

$this->expectException(string $exceptionClassName);
$this->expectExceptionCode(int|string $code);
$this->expectExceptionMessage(string $message);
$this->expectExceptionMessageMatches(string $regularExpression);
$this->expectExceptionObject(\Exception $exceptionObject);

必须在之前一个代码,在该代码中预计会抛出一个异常,与一个设置了实际值之后的断言相反。

使用异常测试的正确顺序:

<?php

use PHPUnit\Framework\TestCase;

final class ExceptionTest extends TestCase
{
    public function testException(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        throw new \InvalidArgumentException();
    }
}

因为必须在抛出异常之前调用 PHPUnit 内部方法来测试异常,所以与测试异常相关的 PHPUnit 方法从 $this->excpect 而不是 $this->assert 开始是有意义的。

已经知道:

<块引用>

一旦抛出异常,PHP 将无法返回到抛出异常行之后的代码行。

您应该能够轻松地发现此测试中的错误:

<?php
namespace VendorName\PackageName;

class ExceptionTest extends \PHPUnit\Framework\TestCase
{
    public function testThrowException(): void
    {
        # Should be OK
        $this->expectException(\RuntimeException::class);
        throw new \RuntimeException();

        # Should Fail
        $this->expectException(\RuntimeException::class);
        throw new \InvalidArgumentException();
    }
}

第一个 $this->expectException() 应该没问题,它在抛出预期的确切异常类之前需要一个异常类,所以这里没有错。

在抛出完全不同的异常之前,应该失败的第二个期望 RuntimeException 类,因此它应该失败,但是 PHPUnit 执行会到达那个地方吗?

测试的输出是:

 ✔ Throw exception

OK (1 test, 1 assertion)

OK

不,如果测试通过,它离 OK 很远,它应该 Fail 在第二个异常上。这是为什么?

注意输出有:

<块引用>

好的(1 个测试,1 个断言)

测试数量正确但只有 1 assertion

应该有 2 个断言 = OKFail 使测试无法通过。

那只是因为 PHPUnit 是在行后执行 testThrowException 完成的:

throw new \RuntimeException();

这是一个在 testThrowException 范围之外到 PHPUnit 捕获 \RuntimeException 并做它需要做的事情的地方的单程票,但无论它能做什么,我们都知道它不能跳回 testThrowException 因此代码:

# Should Fail
$this->expectException(\RuntimeException::class);
throw new \InvalidArgumentException();

永远不会被执行,这就是为什么从 PHPUnit 的角度来看,测试结果是 OK 而不是 Fail

如果您想在同一测试方法中使用多个 $this->expectException() 或混合使用 $this->expectException()$this->expectExceptionMessage() 调用,这不是一个好消息:

<?php
namespace VendorName\PackageName;

class ExceptionTest extends \PHPUnit\Framework\TestCase
{
    public function testThrowException(): void
    {
        # OK
        $this->expectException(\RuntimeException::class);
        throw new \RuntimeException('Something went wrong');

        # Fail
        $this->expectExceptionMessage('This code will never be executed');
        throw new \RuntimeException('Something went wrong');
    }
}

报错:

<块引用>

好的(1 个测试,1 个断言)

因为一旦抛出异常,所有其他与测试异常相关的 $this->expect... 调用将不会被执行,并且 PHPUnit 测试用例结果将只包含第一个预期异常的结果。

如何测试多个异常?

将多个异常拆分为单独的测试:

<?php
namespace VendorName\PackageName;

class ExceptionTest extends \PHPUnit\Framework\TestCase
{
    public function testThrowExceptionBar(): void
    {
        # OK
        $this->expectException(\RuntimeException::class);
        throw new \RuntimeException();
    }

    public function testThrowExceptionFoo(): void
    {
        # Fail
        $this->expectException(\RuntimeException::class);
        throw new \InvalidArgumentException();
    }
}

给出:

 ✔ Throw exception bar
 ✘ Throw exception foo
   ┐
   ├ Failed asserting that exception of type "InvalidArgumentException" matches expected exception "RuntimeException". Message was: "" at

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

FAILURES 应该的。

然而,这种方法在其基本方法上有一个缺点——对于抛出的每个异常,您都需要一个单独的测试。这将导致大量测试只是为了检查异常。

捕获异常并使用断言进行检查

如果在抛出异常后您无法继续执行脚本,您可以简单地捕获预期的异常,然后使用异常提供的方法获取有关它的所有数据,并将其与预期值和断言结合使用:

<?php
namespace VendorName\PackageName;

class ExceptionTest extends \PHPUnit\Framework\TestCase
{
    public function testThrowException(): void
    {
        # OK
        unset($className);
        try {
            $location = __FILE__ . ':' . (string) (__LINE__ + 1);
            throw new \RuntimeException('Something went wrong'); 

        } catch (\Exception $e) {
            $className = get_class($e);
            $msg = $e->getMessage();
            $code = $e->getCode();
        }

        $expectedClass = \RuntimeException::class;
        $expectedMsg = 'Something went wrong';
        $expectedCode = 0;

        if (empty($className)) {
            $failMsg = 'Exception: ' . $expectedClass;
            $failMsg .= ' with msg: ' . $expectedMsg;
            $failMsg .= ' and code: ' . $expectedCode;
            $failMsg .= ' at: ' . $location;
            $failMsg .= ' Not Thrown!';
            $this->fail($failMsg);
        }

        $this->assertSame($expectedClass, $className);
        $this->assertSame($expectedMsg, $msg);
        $this->assertSame($expectedCode, $code);

        # ------------------------------------------

        # Fail
        unset($className);
        try {
            $location = __FILE__ . ':' . (string) (__LINE__ + 1);
            throw new \InvalidArgumentException('I MUST FAIL !'); 

        } catch (\Exception $e) {
            $className = get_class($e);
            $msg = $e->getMessage();
            $code = $e->getCode();
        }

        $expectedClass = \InvalidArgumentException::class;
        $expectedMsg = 'Something went wrong';
        $expectedCode = 0;

        if (empty($className)) {
            $failMsg = 'Exception: ' . $expectedClass;
            $failMsg .= ' with msg: ' . $expectedMsg;
            $failMsg .= ' and code: ' . $expectedCode;
            $failMsg .= ' at: ' . $location;
            $failMsg .= ' Not Thrown!';
            $this->fail($failMsg);
        }

        $this->assertSame($expectedClass, $className);
        $this->assertSame($expectedMsg, $msg);
        $this->assertSame($expectedCode, $code);
    }
}

给出:

 ✘ Throw exception
   ┐
   ├ Failed asserting that two strings are identical.
   ┊ ---·Expected
   ┊ +++·Actual
   ┊ @@ @@
   ┊ -'Something·went·wrong'
   ┊ +'I·MUST·FAIL·!'

FAILURES!
Tests: 1, Assertions: 5, Failures: 1.

FAILURES 是应该的,但是天哪,您是否阅读了以上所有内容?你需要注意清除变量 unset($className); 以检测是否抛出异常,然后这个生物 $location = __FILE__ ... 有一个准确的异常位置以防它没有抛出,然后检查异常是否被抛出抛出 if (empty($className)) { ... } 并使用 $this->fail($failMsg); 表示是否未抛出异常。

使用 PHPUnit 的数据提供者

PHPUnit 有一个有用的机制,称为 Data Provider。数据提供者是一种返回带有数据集的数据(数组)的方法。当 PHPUnit 调用测试方法 testThrowException 时,单个数据集用作参数。

如果数据提供者返回多个数据集,则测试方法将运行多次,每次使用另一个数据集。这在测试多个异常或/和多个异常的属性(如类名、消息、代码)时很有帮助,因为即使:

<块引用>

一旦抛出异常,PHP 将无法返回到抛出异常行之后的代码行。

PHPUnit 将多次运行测试方法,每次都使用不同的数据集,而不是在单个测试方法运行中测试多个异常(会失败)。

这就是为什么我们可能会创建一个测试方法,一次只负责测试一个异常,但使用 PHPUnit 的数据提供程序使用不同的输入数据和预期异常多次运行该测试方法。

数据提供者方法的定义可以通过对应该由数据提供者提供数据集的测试方法进行@dataProvider注解来完成。

<?php

class ExceptionCheck
{
    public function throwE($data)
    {
        if ($data === 1) {
            throw new \RuntimeException;
        } else {
            throw new \InvalidArgumentException;
        }
    }
}

class ExceptionTest extends \PHPUnit\Framework\TestCase
{
    public function ExceptionTestProvider() : array
    {
        $data = [
            \RuntimeException::class =>
            [
                [
                    'input' => 1,
                    'className' => \RuntimeException::class
                ]
            ],

            \InvalidArgumentException::class =>
            [
                [
                    'input' => 2,
                    'className' => \InvalidArgumentException::class
                ]
            ]
        ];
        return $data;
    }

    /**
     * @dataProvider ExceptionTestProvider
     */
    public function testThrowException($data): void
    {
        $this->expectException($data['className']);
        $exceptionCheck = new ExceptionCheck;

        $exceptionCheck->throwE($data['input']);
    }
}

给出结果:

 ✔ Throw exception with RuntimeException
 ✔ Throw exception with InvalidArgumentException

OK (2 tests, 2 assertions)

请注意,即使整个 ExceptionTest 中只有一个测试方法,PHPUnit 的输出也是:

<块引用>

OK(2 个测试,2 个断言)

所以连线:

$exceptionCheck->throwE($data['input']);

第一次抛出异常,用相同的测试方法测试另一个异常没有问题,因为由于数据提供者,PHPUnit再次使用不同的数据集运行它。

数据提供者返回的每个数据集都可以命名,您只需要使用一个字符串作为键,在该键下存储一个数据集。因此预期的异常类名被使用了两次。作为数据集数组的键和值(在 'className' 键下),稍后用作 $this->expectException() 的参数。

使用字符串作为数据集的键名使这个漂亮且不言自明的摘要:

<块引用>

✔ 使用 RuntimeException

抛出异常

✔ 使用 InvalidArgumentException

抛出异常

如果你改变行:

if ($data === 1) {

到:

if ($data !== 1) {

public function throwE($data)

要抛出错误的异常并再次运行 PHPUnit,您会看到:

 ✘ Throw exception with RuntimeException
   ├ Failed asserting that exception of type "InvalidArgumentException" matches expected exception "RuntimeException". Message was: "" at (...)

 ✘ Throw exception with InvalidArgumentException
   ├ Failed asserting that exception of type "RuntimeException" matches expected exception "InvalidArgumentException". Message was: "" at (...)

FAILURES!
Tests: 2, Assertions: 2, Failures: 2.

正如预期的那样:

<块引用>

失败! 测试:2,断言:2,失败:2。

准确指出导致一些问题的数据集名称:

<块引用>

✘ 使用 RuntimeException

抛出异常

✘ 使用 InvalidArgumentException

抛出异常

使 public function throwE($data) 不抛出任何异常:

public function throwE($data)
{
}

再次运行 PHPUnit 给出:

 ✘ Throw exception with RuntimeException
   ├ Failed asserting that exception of type "RuntimeException" is thrown.

 ✘ Throw exception with InvalidArgumentException
   ├ Failed asserting that exception of type "InvalidArgumentException" is thrown.

FAILURES!
Tests: 2, Assertions: 2, Failures: 2.

看起来使用数据提供者有几个优点:

  1. 输入数据和/或预期数据与实际测试方法分开。
  2. 每个数据集都可以有一个描述性名称,清楚地指出哪些数据集导致测试通过或失败。
  3. 如果测试失败,您会收到一条正确的失败消息,指出没有抛出异常或抛出了错误的异常,而不是断言 x 不是 y。
  4. 测试一个可能抛出多个异常的方法只需要一个测试方法。
  5. 可以测试多个异常和/或多个异常的属性,例如类名、消息、代码。
  6. 不需要任何非必要的代码,如 try catch 块,只需使用内置的 PHPUnit 功能即可。

测试异常问题

“TypeError”类型的异常

使用 PHP7 数据类型支持此测试:

<?php
declare(strict_types=1);

class DatatypeChat
{
    public function say(string $msg)
    {
        if (!is_string($msg)) {
            throw new \InvalidArgumentException('Message must be a string');
        }
        return "Hello $msg";
    }
}

class ExceptionTest extends \PHPUnit\Framework\TestCase
{
    public function testSay(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $chat = new DatatypeChat;
        $chat->say(array());
    }
}

输出失败:

 ✘ Say
   ├ Failed asserting that exception of type "TypeError" matches expected exception "InvalidArgumentException". Message was: "Argument 1 passed to DatatypeChat::say() must be of the type string, array given (..)

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

即使在方法 say 中有:

if (!is_string($msg)) {
   throw new \InvalidArgumentException('Message must be a string');
}

并且测试通过一个数组而不是一个字符串:

$chat->say(array());

PHP 未到达代码:

throw new \InvalidArgumentException('Message must be a string');

因为类型输入 string 导致异常提前抛出:

public function say(string $msg)

因此抛出 TypeError 而不是 InvalidArgumentException

再次出现“TypeError”类型的异常

知道我们不需要 if (!is_string($msg)) 来检查数据类型,因为如果我们在方法声明中指定数据类型 say(string $msg) 我们可能想抛出 {{1} } 如果消息太长InvalidArgumentException

if (strlen($msg) > 3)

还要修改 <?php declare(strict_types=1); class DatatypeChat { public function say(string $msg) { if (strlen($msg) > 3) { throw new \InvalidArgumentException('Message is too long'); } return "Hello $msg"; } } class ExceptionTest extends \PHPUnit\Framework\TestCase { public function testSayTooLong(): void { $this->expectException(\Exception::class); $chat = new DatatypeChat; $chat->say('I have more than 3 chars'); } public function testSayDataType(): void { $this->expectException(\Exception::class); $chat = new DatatypeChat; $chat->say(array()); } } 所以我们有两种情况(测试方法)应该抛出 ExceptionTest - 第一个 Exception 当消息太长时第二个 testSayTooLong 当消息类型错误。

在这两个测试中,我们期望使用一个通用的 testSayDataType 类,而不是像 InvalidArgumentExceptionTypeError 这样的特定异常类

Exception

测试结果为:

$this->expectException(\Exception::class);

✔ Say too long ✘ Say data type ├ Failed asserting that exception of type "TypeError" matches expected exception "Exception". Message was: "Argument 1 passed to DatatypeChat::say() must be of the type string, array given (..) FAILURES! Tests: 2, Assertions: 2, Failures: 1. 期待通用 testSayTooLong() 并使用

Exception

在抛出 $this->expectException(\Exception::class); 时以 OK 传递

但是

InvalidArgumentException 使用相同的 testSayDataType() $this->expectException(\Exception::class); 和描述:

<块引用>

未能断言“TypeError”类型的异常与预期的异常“异常”匹配。

PHPUnit 抱怨 exception Fails 不是 TypeError,这看起来令人困惑,否则 Exception 在 {{ 1}} 因为它对 $this->expectException(\Exception::class); 抛出 testSayDataType() 并期待:testSayTooLong()

没有任何问题

问题是 PHPUnit 使用上述描述误导了您,因为 InvalidArgumentException 不是例外。 $this->expectException(\Exception::class); 不继承自 TypeError 类或其任何其他子类。

TypeError 实现 Exception 接口见 documentation

TypeError 扩展 Throwable documentation

InvalidArgumentException 扩展 LogicException documentation

因此 LogicException 也扩展了 Exception

这就是为什么抛出 InvalidArgumentException 通过 OK 和 Exception 的测试,但抛出 InvalidArgumentException 不会(它不扩展 $this->expectException(\Exception::class);

然而 TypeErrorException 都实现了 Exception 接口。

因此在两个测试中都发生了变化

TypeError

Throwable

使测试变绿:

$this->expectException(\Exception::class);

See the list of Errors and Exception classes and how they are related to each other

需要明确一点:在单元测试中使用特定的异常或错误而不是通用的 $this->expectException(\Throwable::class); ✔ Say too long ✔ Say data type OK (2 tests, 2 assertions) 是一种很好的做法,但是如果您现在遇到有关异常的误导性评论会知道为什么 PHPUnit 的异常 Exception 或其他异常错误实际上不是 Throwable 而是 TypeError

答案 12 :(得分:0)

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

}

答案 13 :(得分: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 

   }

}

答案 14 :(得分: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/');

如果我们在匿名函数中键入的代码未引发异常,则将引发异常。

如果我们在匿名函数中键入的代码引发异常,但其消息与预期的正则表达式不匹配,则也会引发异常。