How do I test for an exact Exception message, rather than a substring, with PHPUnit?

谁说胖子不能爱 提交于 2020-06-08 16:51:42

问题


According to the PHPUnit Documentation on @expectedExceptionMessage, the string must only be a substring of the actual Exception thrown.

In one of my validation methods, an array item is pushed for each error that occurs, and the final Exception message is displayed by imploding the array of errors.

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

The problem I have here is that in the PHPUnit test method I check for different combinations. This causes tests to pass that I intend to fail.

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

The string of the Exception message is actually "a < b. b < c." but this test still passes. I intend for this test to fail because the message is not exactly what I expect.

Is there a way with PHPUnit to expect an exact string, rather than a substring? I hope to avoid the following:

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.");
}

回答1:


When this question was posted, PHPUnit v3.7 didn't have a solution to this problem. Newer versions have a new @expectedExceptionMessageRegExp option that you can use to add a regular expression to match the exception message against.

Your case, using ^ and $ to force the string to be exactly what is expected, could look like this:

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



回答2:


I tried many ways to test exceptions and finally I found that the best way to test Exceptions is try-catch blocks. I suggest you to throw exceptions with exception code and then test if exception with this code is thrown. For example suppose that your exception code is 101

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

For example suppose that your exception code is 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());
}



回答3:


If your test class extends PHPUnit_Framework_TestCase, you should be able to use setExpectedException method:

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

It allows you to assert an exception class alone, exception message and even exception code.




回答4:


You can use $this->expectExceptionMessage to handle this.

Example:

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



回答5:


I normally do not worry about the text of the exception, but that the exception itself is thrown, so that an @expectedException can be used, and even a Data Provider for most error scenarios.

The other change I make then is to do the tests (a < b) in a different function, and if a failure occurs, throw the exception, instead of combining them all and looking at the text. The different functions can then be tested on their own as well.

From reading the PHPUnit manual, even the error thrown on assert uses the word 'contains', so it implies that it is a substring match. The same manual suggests writing the tests as you did above, so I believe that as long as you want to check the exception text, then according to the samples in the PHPUnit manual, you need to write the tests as you did.

I would also caution from comparing the actual text, in case you add multiple language support in the future, the test will need to then understand the different languages/sentences returned. Simply checking the exception is thrown, will not suffer this problem.




回答6:


Another possible solution, to make sure that error have happened:

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


来源:https://stackoverflow.com/questions/18647836/how-do-i-test-for-an-exact-exception-message-rather-than-a-substring-with-phpu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!