有谁知道是否有 assert
或类似的东西可以测试在被测试的代码中是否引发了异常?
<?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 提供了有关测试异常最佳做法的详细说明。
在 PHPUnit 9 发布之前,您还可以使用 docblock annotation:
class ExceptionTest extends PHPUnit_Framework_TestCase
{
/**
* @expectedException InvalidArgumentException
*/
public function testException()
{
...
}
}
对于 PHP 5.5+(尤其是命名空间代码),我现在更喜欢使用 ::class
IncorrectPasswordException
应该足够了 - 等于 "Wrong password for bob@me.com"
的消息是辅助的。再加上您希望花尽可能少的时间编写测试,您就会开始看到简单测试变得多么重要。
另一种方法可以如下:
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Expected Exception Message');
请确保您的测试类范围为 \PHPUnit_Framework_TestCase
。
expectExceptionMessage
表现为正则表达式。如果您的错误消息是“Foo bar Baz”,$this->expectExceptionMessage('Foo');
将使测试通过。
expectExceptionCode(401)
expectExceptionMessage($message)
就会起作用。 PHPUnit 在内部使用 strpos
来评估条件。
如果您在 PHP 5.5+ 上运行,则可以使用 ::class
resolution 通过 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
进入
"\My\Cool\Package\WrongPasswordException"
没有 PHPUnit 更明智。
注意:PHPUnit 5.2 引入了 expectException 作为 setExpectedException 的替代品。
下面的代码将测试异常消息和异常代码。
重要提示:如果没有抛出预期的异常,它将失败。
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());
}
$this->fail()
不应该以这种方式使用我不认为,至少目前不是(PHPUnit 3.6.11);它本身就是一个例外。使用您的示例,如果调用 $this->fail("Expected exception not thrown")
,则触发 catch
块并且 $e->getMessage()
是“未引发预期异常”。
fail
的调用可能属于 在 catch 块之后,而不是在 try 内。
fail
的调用不应该在 try
块中。它本身会触发 catch
块产生错误结果。
catch(Exception $e)
捕获所有异常。当我尝试捕获特定异常时,此方法对我来说效果很好:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
您可以使用 assertException extension 在一次测试执行期间断言多个异常。
将方法插入您的 TestCase 并使用:
public function testSomething()
{
$test = function() {
// some code that has to throw an exception
};
$this->assertException( $test, 'InvalidArgumentException', 100, 'expected message' );
}
我还为漂亮代码的爱好者制作了一个 trait。
assertException
。我也无法在 PHPUnit 手册中找到它。
asertException
方法不是原始 PHPUnit 的一部分。您必须继承 PHPUnit_Framework_TestCase
类并手动添加 method linked in post above。然后,您的测试用例将继承这个继承的类。
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 个断言 = OK
和 Fail
使测试不通过。
这仅仅是因为 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 的输出也是:
好的(2 个测试,2 个断言)
所以即使是这条线:
$exceptionCheck->throwE($data['input']);
第一次抛出异常,使用相同的测试方法测试另一个异常没有问题,因为 PHPUnit 再次使用不同的数据集运行它,这要归功于数据提供者。
数据提供者返回的每个数据集都可以命名,您只需使用一个字符串作为存储数据集的键。因此预期的异常类名被使用了两次。作为数据集数组的键和值(在“类名”键下),稍后用作 $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.
看起来使用数据提供者有几个优点:
输入数据和/或预期数据与实际测试方法分开。每个数据集都可以有一个描述性的名称,清楚地指出是什么数据集导致测试通过或失败。如果测试失败,您会收到一条正确的失败消息,指出没有抛出异常或抛出了错误的异常,而不是断言 x 不是 y。测试可能引发多个异常的单个方法只需要一个测试方法。可以测试多个异常和/或多个异常的属性,如类名、消息、代码。不需要像 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))
来检查数据类型,因为 PHP 已经注意到如果我们在方法声明中指定数据类型 say(string $msg)
如果消息太长,我们可能想要抛出 InvalidArgumentException
{4 }。
<?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
当消息类型错误时。
在这两个测试中,我们期望通过使用而不是像 InvalidArgumentException
或 TypeError
这样的特定异常类只是一个通用 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);
抛出 InvalidArgumentException
时通过 OK
但
testSayDataType()
使用与描述相同的 $this->expectException(\Exception::class);
Fails
:
断言“TypeError”类型的异常与预期的异常“Exception”匹配失败。
PHPUnit 抱怨 exception TypeError
不是 Exception
看起来令人困惑,否则它不会对 testSayDataType()
中的 $this->expectException(\Exception::class);
有任何问题,因为它没有任何问题testSayTooLong()
抛出 InvalidArgumentException
并期待:$this->expectException(\Exception::class);
问题是 PHPUnit 用上面的描述误导了你,因为 TypeError
不是 一个例外。 TypeError
不从 Exception
类或其任何其他子类扩展。
TypeError
实现 Throwable
接口参见 documentation
然而
InvalidArgumentException
扩展 LogicException
documentation
和 LogicException
扩展 Exception
documentation
因此 InvalidArgumentException
也扩展了 Exception
。
这就是为什么用 OK 和 $this->expectException(\Exception::class);
抛出 InvalidArgumentException
通过测试但抛出 TypeError
不会(它不会扩展 Exception
)
但是 Exception
和 TypeError
都实现了 Throwable
接口。
因此在两个测试中都发生了变化
$this->expectException(\Exception::class);
至
$this->expectException(\Throwable::class);
使测试绿色:
✔ Say too long
✔ Say data type
OK (2 tests, 2 assertions)
See the list of Errors and Exception classes and how they are related to each other。
需要明确一点:在单元测试中使用特定的异常或错误而不是通用的 Exception
或 Throwable
是一种很好的做法,但是如果您现在遇到关于异常的误导性评论,您就会知道为什么 PHPUnit 的异常 {3 } 或其他异常错误实际上不是 Exception
,而是 Throwable
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');
});
}
$this->expectException()
测试多个异常。 More info
综合解决方案
PHPUnit 当前用于异常测试的“best practices”似乎……乏善可陈(docs)。
由于我 wanted more 比当前的 expectException
实现,我制作了一个 trait 用于我的测试用例。只有 ~50 lines of code。
每个测试支持多个异常
支持抛出异常后调用的断言
强大而清晰的使用示例
标准断言语法
不仅支持消息、代码和类的断言
支持逆断言,assertNotThrows
支持 PHP 7 Throwable 错误
图书馆
我将 AssertThrows
特征发布到 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();
});
}
}
?>
public function testException() {
try {
$this->methodThatThrowsException();
$this->fail("Expected Exception has not been raised.");
} catch (Exception $ex) {
$this->assertEquals("Exception message", $ex->getMessage());
}
}
assertEquals()
的签名是 assertEquals(mixed $expected, mixed $actual...)
,与您的示例相反,所以它应该是 $this->assertEquals("Exception message", $ex->getMessage());
这是您可以执行的所有异常断言。请注意,它们都是可选的。
class ExceptionTest extends PHPUnit_Framework_TestCase
{
public function testException()
{
// make your exception assertions
$this->expectException(InvalidArgumentException::class);
// if you use namespaces:
// $this->expectException('\Namespace\MyException');
$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 中找到文档。
/**
* @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 才能使用作曲家进行更新。
对于 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());
}
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
}
}
PhpUnit 是一个了不起的库,但是这一点有点令人沮丧。这就是为什么我们可以使用 turbotesting-php 开源库,它有一个非常方便的断言方法来帮助我们测试异常。在这里可以找到:
要使用它,我们只需执行以下操作:
AssertUtils::throwsException(function(){
// Some code that must throw an exception here
}, '/expected error message/');
如果我们在匿名函数中键入的代码没有抛出异常,就会抛出异常。
如果我们在匿名函数中键入的代码抛出异常,但其消息与预期的正则表达式不匹配,也会抛出异常。
不定期副业成功案例分享
$this->setExpectedException('\My\Name\Space\MyCustomException');
setExpectedException
方法已弃用,替换为expectException
方法。 :)expectException()
之后调用。虽然这对某些人来说可能很明显,但对我来说这是一个陷阱。