PHPUnit: Where should I throw the exception (expectException method)? - php

I know both versions are correct but I would like to know which is "better".
The problem with expectException() method is it is written before you type the method which launches the exception.
My question is, should I put them at the beginning of the method (to make them more visible) or otherwise only just before the method which causes the exception (I think it has more sense)?
Option A)
/** #test */
public function shouldThrowsAnException(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Error message');
$foo = new Foo();
$foo->bar(); // <-- This method launches the exception!!
}
Option B)
/** #test */
public function shouldThrowsAnException(): void
{
$foo = new Foo();
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Error message');
$foo->bar(); // <-- This method launches the exception!!
}

When writing unit tests, each unit should be testing only one thing. So your expectException should be as specific as possible, as you have in your code, looking for a particular exception and a particular message.
If your constructor happened to throw the same exception, then Option B would be the better option, so you don't catch the constructor exception by accident. If the constructor does throw exceptions, you would then write other test cases that flush out the constructor.
If the constructor does not throw any exceptions, the two unit test are the same as far as execution.
If you were asking from a readability standpoint, then Option B is more readable, because it is clear that the expectation is that your method Bar will be throwing the exception.

Related

Unit Test for Parent Method Call

Weirdly, not only have I failed to find an answer for this, but also this question doesn't seem to be a concern at all. It is for me, however.
How do you set an expectation of a parent method call with PHPUnit?
I already read the answers and discussions, the point of which is that "you don't need to test for that, you only test that it works". This is entirely wrong. Unit tests are there to test for the internals of the implementation. Testing that "it works" is for functional tests.
More context: I have an exception implementation, which accepts additional arguments to the constructor, and therefore naturally needs to call the parent::__construct() method, forwarding the message, the code, and the inner exception to it. I know that the parent method works, and I don't need to test it: it's a native PHP class, and even if it wasn't, that code is not in the class I am testing. So, all I need is to set some expectations, one of which is that the parent __construct() method is called with the appropriate args. Of course, the constructor is not the only case where this could be necessary.
Any ideas?
As for the PHPUnit and checking for parent methods calls it is unfortunately impossible. You can always create a mock of a class and check if the parent constructor was called but then you are only testing a mock and not your actual class. And this is definitely not what you want. As someone else already pointed out in another answer: you don't mock or stub subject-under-test (https://stackoverflow.com/a/6711948/4737924).
You are saying that you need to know is that the parent constructor was called. I think it is only a technical detail of the implementation. What you really need to know is if your exception behaves as it needs to behave.
Take a look at the following example:
class MyException extends Exception
{
private $somethingElse;
public function __construct(string $message, int $code, Throwable $previousException, SomethingElse $somethingElse)
{
$message = $message . ' - my additional message';
parent::__construct($message, $code, $previousException);
$this->somethingElse = $somethingElse;
}
public function getSomethingElse(): SomethingElse
{
return $this->somethingElse;
}
}
class MyExceptionTest extends TestCase
{
public function testIsCreatedProperly(): void
{
$previousException = new Exception();
$somethingElse = new SomethingElse();
$exception = new MyException('My message', 100, $previousException, $somethingElse);
$this->assertSame('My message - my additional message', $exception->getMessage());
$this->assertSame(100, $exception->getCode());
$this->assertSame($previousException, $exception->getPrevious());
$this->assertSame($somethingElse, $exception->getSomethingElse());
}
}
Here only the behavior matters. It is really irrelevant how it happened that the message is correct, or the code or the previous exception. All it matters is if it behaves as you wanted it to behave. You don't need to explicitly check if the parent constructor was called. You know it was called because the output is as expected.
You can create smth like the following trait, and use it in all classes, where you need to mock parent method call:
trait ParentCallTestable
{
/**
* Call parent methdod
* #param string $method Method name to call
* #return mixed
*/
public function parent($method, ...$args)
{
return parent::$method(...$args);
}
}
Then in a class, that uses this trait, standard parent call
$result = parent::fooMethod($arg1, $arg2);
is replaced with
$result = $this->parent('fooMethod', $arg1, $arg2);
and you can mock it easily.

How to cover Exception Classes in PHPUnit

Im aiming for very high code coverage and want to either cover exception classes or have them excluded from the codecoverage reports.
Example code
class My_DataException extends Exception
{}
class Foo
{
function __construct() { throw new My_DataException('see?'); }
}
How can I either get code coverage on My_DataException (in library/My/DataException.php) or have the file excluded from appearing in the code coverage report? I'd prefer not to use the annotation method (#codeCoverageIgnore or something like that).
My phpunit.xml has a black list defined, and no whitelist
<filter>
<blacklist>
<directory suffix="Exception.php">../library/</directory>
</blacklist>
</filter>
Every Exception file will end in "Exception.php" so im not sure why the suffix portion doesnt work.
Additional Details:
This is not a question about the right amount of coverage
I would prefer to not use the annotation method in each Exception file
Every Exception file will end in "Exception.php" eg. My/Exception.php or My/DataException.php
For the following code:
class Foo
{
function __construct() {
throw new My_DataException('see?');
}
}s
You will get code coverage if you execute that line in tests:
new Foo;
For such a test you can tell Phpunit which exception you expect with an annotation:
/**
* #expectedException My_DataException
*/
However Exceptions are normally exceptions so you do not cover them but there can be in there for safety reasons too and you yet do not know how you can trigger them with test-setup / data / parameters.
Then think harder and try to trigger them. Otherwise it might be that the code is superfluous because technically you are not able to throw the exception, hence it is not necessary.
For the cases you know they can happen but you still can not trigger them (is that possible?) you can mark certain areas of your script to be excluded from coverage reporting in the source code:
// #codeCoverageIgnoreStart
throw new My_DataException('see?');
// #codeCoverageIgnoreEnd
Use it sparsely you might want to remove this in the future.
I was looking for a way to cover the actual exception files, this is how I eventually stumbled upon the answer:
<?php
/**
* Simple test for exception
*/
class Api_ExceptionTest extends PHPUnit_Framework_TestCase
{
/**
* Test can construct the exception, then throw it.
*
* #expectedException Api_Exception
*/
public function testThrowException()
{
$exception = new Api_Exception();
throw $exception;
}
}

How to document #throws in interface documentation

I'm writing a PHP library and I have a concern. I have something similar to the following in my interfaces:
<?php
/**
* My interface
*
* ...
*/
interface MyInterface
{
/**
* This method does foo.
*
* #throws \RuntimeException If foo can't be done.
*/
public function fooAndBar();
}
?>
Now, the #throwsentry isn't perfectly right, since an interface doesn't actually do anything, and is used purely to abstract implementation details. However, I've always used it because all my implementations of the interface thrown the exception when something goes wrong.
But another developer might write an implementation that can't fail (so it can't throw an exception), or he/she might want to use another exception class.
In this situation, how should I document #throws in interface declarations? Should it even be documented?
Consider code where you consume the interface:
public function doSomething(MyInterface $my) { ... }
If even one of the implementations can throw an exception, you'll want to make sure you handle the possibility of exceptions.
So, yes, it should be documented.
Even if only one implementation throws an exception, exception handling still needs to be in place. Of course this doesn't mean that every method should have a #throws slapped on it. It should still only be used where appropriate (where you're expecting an implementation to legitimately need to throw an exception).
As a more concrete example, consider the following:
interface LogWriter
{
/**
* #throws LogWriterException
*/
public function write($entry);
}
class DbLogWriter
{
public function __construct(PDO $db)
{
//store $db somewhere
}
public function write($entry)
{
try {
//store $entry in the database
} catch (PDOException $e) {
throw new LogWriterException(...);
}
}
}
class NullLogWriter
{
public function write($entry) { }
}
Certain things could be done to attempt to lower the probability of an exception when writing to the database, but at the end of the day, it's not an exception safe operation. Therefore, DbLogWriter::write should be expected to throw exceptions.
Now consider the null writer though, that just discards entries. There's absolutely nothing at all that could ever go wrong there, therefore, no need for exceptions.
Yet what if you have some $log and all you know about it is that it's an implementation of LogWriter. Do you assume it doesn't throw exceptions and potentially accidentally let one bubble up, or do you assume that it can throw a LogWriterException? I would stay on the safe side and assume that it can throw a LogWriterException.
If all the user knows is that the $log is a LogWriter but only DbLogWriter is documented as throwing an exception, the user may not realize that $log->write(...) can throw an exception. Also, when FileLogWriter is later created, it would mean the expectations of what exceptions that implementation can and possibly will throw will already be set (no one would expect the FileLogWriter to throw a RandomNewException).
Interfaces define contracts. Whether an implementing class throws an Exception is an implementation detail in PHP because there is no throws keyword in the method signature (like in Java). Adding a #throws annotation cannot enforce the contract technically, but it can indicate convention (same for return values btw). Whether that is good enough is up to you to decide.
On a sidenote, if a developer comes up with an implementation that doesn't throw you dont have a problem because you will have to add a try/catch block anyway for those implementations that do throw (by convention). It would be a problem if an implementation starts throwing a different Exception than indicated in the DocBlock because then it wouldn't be catched.

PHPUnit doesn't catch expected exceptions

I have a set of tests, and I want to test that my classes throw exceptions at the right time. In the example, my class uses the __get() magic method, so I need to test that an exception is thrown when an invalid property is retrieved:
function testExceptionThrownWhenGettingInvalidProperty() {
$obj = new MyClass();
$this->setExpectedException("Property qwerty does not exist");
$qwerty = $obj->qwerty;
}
The class throws an error as it should, but instead of just getting a pass, the exception isn't caught!
There was 1 error:
1) QueryTest::testExceptionThrownWhenGettingInvalidProperty
Exception: Property qwerty does not exist
I was using SimpleTest before, and $this->expectException(new Exception("Property qwerty does not exist")); worked just fine. I know there are other methods (#expectedException and try-catch), but this one should work and it looks a lot cleaner. Any ideas how I can make this work?
It's not looking for the text in the exception, it's looking for the name of the exception class... Docs
$this->setExpectedException('Exception');
It's quite handy when you're using SPL Exceptions, or custom exception classes...
Adding to ircmaxell's answer, there is actually a simpler way of doing this:
/**
* #expectedException MyExceptionClass
* #expectedExceptionMessage Array too small
*/
public function testSomething()
{
}
The #expectedException the class name of the exception to expect, and #expectedExceptionMessage is a substring of the exception message to expect (that's right, you don't have to have the entire message).
If you prefer to not use docblock annotations, both of these are actually available as methods on the test case.

Why is PHPUnit ignoring assertions when expecting an exception?

I have a simple unit test case (extensive question here) on a configuration class that by design triggers PHP errors on type mismatches and undefined configuration settings. In addition to the error, the method is exited returning false.
In my test case, I want to have a number of tests that fail. Trouble is, I can't do a simple 'assert' on the return value: Every error thrown during running a test will be converted into a PHPUnit_Framework_Error exception.
Now I can make PHPUnit aware that I am expecting an error:
/**
* #expectedException PHPUnit_Framework_Error
*/
public function testSetInvalidKey()
{
$this->assertEquals(true, conf::set("invalid_key", "12345"));
}
This works fine, but what I don't understand is that any additional assertion within that function will not be evaluated. Whether it would fail or not, PHPUnit seems to only wait for the exception to happen, and be satisfied with the whole function when it has happened.
To wit, this test will run OK:
/**
* #expectedException PHPUnit_Framework_Error
*/
public function testSetInvalidKey()
{
// The error will be triggered here
$this->assertEquals(true, conf::set("invalid_key", "12345"));
$this->assertEquals(12345, 67810); // Huh?
$this->assertEquals("abc", "def"); // Huh?
$this->assertEquals(true, false); // Huh?
}
Why? Is this the intended behaviour?
I realize you would just separate the assertions into different functions, but I would like to understand the behaviour.
Since conf::set() is executed within the method testSetInvalidKey() the corresponding catch block must be outside. Once caught and logged as the expected exception, I don't see how PHP could resume execution after the first assertion.
Extreme psuedo-code:
class Tester
{
public function run()
{
try {
$test->testSetInvalidKey();
}
catch (PHPUnit_Framework_Error $e) {
// Expected exception caught! Woohoo!
// How can I continue to run the above method where I left off?
}
}
}
This type of behavior would be a great proponent to those who believe in the 1 assertion per test axiom.
php unit has the ability to test for exceptions. Take a look at:
http://www.phpunit.de/manual/3.2/en/writing-tests-for-phpunit.html

Categories