问题
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