如何使用PHPUnit测试精确的Exception消息而不是子字符串?

时间:2013-09-06 00:03:48

标签: php unit-testing exception-handling phpunit php-5.3

根据@expectedExceptionMessage上的PHPUnit Documentation,字符串必须只是抛出的实际Exception的子字符串。

在我的一个验证方法中,针对发生的每个错误推送一个数组项,并通过插入错误数组来显示最终的Exception消息。

class MyClass
{
    public function validate($a, $b, $c, $d)
    {
        if($a < $b) $errors[] = "a < b.";
        if($b < $c) $errors[] = "b < c.";
        if($c < $d) $errors[] = "c < d.";

        if(count($errors) > 0) throw new \Exception(trim(implode(" ", $errors)));
    }
}

我在这里遇到的问题是在PHPUnit测试方法中我检查了不同的组合。这导致测试通过我打算失败。

/**
 * @expectedException \Exception
 * @expectedExceptionMessage a < b.
 */
public function testValues_ALessBOnly()
{
    $myClass = new MyClass()
    $myClass->validate(1, 2, 4, 3);
}

Exception消息的字符串实际上是"a < b. b < c.",但此测试仍然通过。我打算让这个测试失败,因为这个消息并不完全符合我的预期。

有没有办法让PHPUnit期望一个完整的字符串,而不是一个子字符串?我希望避免以下内容:

public function testValues_ALessBOnly()
{
    $myClass = new MyClass()
    $fail = FALSE;

    try
    {
        $myClass->validate(1, 2, 4, 3);
    }
    catch(\Exception $e)
    {
        $fail = TRUE;
        $this->assertEquals($e->getMessage(), "a < b.";
    }

    if(!$fail) $this->fail("No Exceptions were thrown.");
}

6 个答案:

答案 0 :(得分:5)

发布此问题时,PHPUnit v3.7没有解决此问题的方法。较新的版本有一个新的@expectedExceptionMessageRegExp选项,您可以使用该选项添加正则表达式以匹配异常消息。

使用^ and $强制字符串完全符合预期的情况,您的情况可能如下所示:

/**
 * @expectedException \Exception
 * @expectedExceptionMessageRegExp /^a < b\.$/
 */
public function testValues_ALessBOnly()
{
    $myClass = new MyClass()
    $myClass->validate(1, 2, 4, 3);
}

答案 1 :(得分:1)

我尝试了很多方法来测试异常,最后我发现测试Exceptions的最佳方法是try-catch块。我建议您使用异常代码抛出异常,然后测试是否抛出了此代码的异常。例如,假设您的异常代码为101

if(count($errors) > 0) throw new \Exception(trim(implode(" ", $errors)), 101);

例如,假设您的异常代码为101

try {
    $myClass->validate(1, 2, 4, 3);
    $this->fail( "Exception with 101 code should be thrown" );
} catch (Exception $e) {
    $this->assertEquals( 101, $e->getCode());
}

答案 2 :(得分:1)

如果您的测试类延伸PHPUnit_Framework_TestCase,您应该可以使用setExpectedException方法:

    /**
     * @param mixed  $exceptionName
     * @param string $exceptionMessage
     * @param int    $exceptionCode
     *
     * @since  Method available since Release 3.2.0
     */
    public function setExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = null)
    {
        $this->expectedException        = $exceptionName;
        $this->expectedExceptionMessage = $exceptionMessage;
        $this->expectedExceptionCode    = $exceptionCode;
    }

它允许您单独断言异常类,异常消息甚至异常代码。

答案 3 :(得分:1)

您可以使用$this->expectExceptionMessage来解决这个问题。

示例:

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage = "Some error message";

答案 4 :(得分:0)

我通常不担心异常的文本,但抛出异常本身,因此可以使用@expectedException,甚至可以使用数据提供程序来处理大多数错误情况。

我做的另一个改变是在不同的函数中进行测试(a&lt; b),如果发生故障,抛出异常,而不是将它们全部组合并查看文本。然后可以自己测试不同的功能。

从阅读PHPUnit手册,即使assert上抛出的错误也使用了'contains'这个词,所以它暗示它是一个子串匹配。同样的手册建议你像上面那样编写测试,所以我相信只要你想检查异常文本,那么根据PHPUnit手册中的示例,你需要像你一样编写测试。

我也会在比较实际文本时提醒,如果将来添加多种语言支持,测试将需要了解返回的不同语言/句子。只需检查异常,就不会遇到这个问题。

答案 5 :(得分:0)

另一种可能的解决方案,以确保已发生错误:

        $errorHappened = false;
        try {
            //call your code here 
        } catch (\Exception $e) {
            $errorHappened = true;
            $this->assertEquals($e->getMessage(), "Expected error text");
            $this->assertEquals($e->getCode(), "Expected error code");
        }
        $this->assertTrue($errorHappened);