PHPUnit doesn't catch expected exceptions - php

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.

Related

PHPUnit and throwing exceptions using Reflection

I'm not necessarily look for answer involving specifically Reflection, any answer that works will do.
I have the following abstract class which is extended by another class:
abstract class A_Class{
protected function returnSomething(array $param = ['some_argument' => false])
{
if(!is_bool($param))
{
throw new Exception('some message goes here');
}
}
}
class B_Class extends A_Class{}
And I'm using PHPUnit 4.8.27 by Sebastian Bergmann and contributors..
I have the following test
/**
* #expectedException \Exception
*/
public function testException()
{
$method = new ReflectionMethod(B_Class::class, 'returnSomething');
$method->setAccessible(true);
$method->invokeArgs(new B_Class(), ['some_argument' => 'string']);
}
When I run my tests the following message appears:
Failed asserting that exception of type "\Exception" is thrown.
I've google'd around a bit, I can't really find and answer as to what I'm doing wrong. To be perfectly honest I'm not even sure that I'm doing anything wrong. The problem itself may not be with my code as much as it is with the Reflection class. I don't know much about it and all the documentation is kind of, uhm, lacking. It may not be able to throw the exception defined in the reflected class.
Any pointers in the right direction here would be greatly appreciated.
What I've tried so far:
Using ReflectionClass instead of ReflectionMethod:
/**
* #expectedException \Exception
*/
public function testGetExcerptException()
{
$method = new ReflectionClass(new B_Class()::class);
$methodToCall = $method->getMethod('returnSomething');
$methodToCall->setAccessible(true);
$methodToCall->invokeArgs(new B_Class(), ['some_argument' => 'string']);
}
Setting the visibility to public, which of course works, but that kind of defeats the purpose.
In case anybody comes across this question. Don't do what I did. Even the The guy that wrote PHPUnit says it's a bad idea. All tested methods should be public.
An alternative solution from using Reflection, since you are using PHPUnit, is to use MockObjects.
Mocks in PHPUnit allow you to mock any public and protected methods for your classes.

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.

PHP: Namespaced classes, class loading and exceptions causes weird behaviour

I witnessed some strange behaviour regarding PHP's exception handling in a recent project. Case goes as follows.
In my app, I use namespaces. All classes are in individual source code files. The code relevant to this particular case, is spread over 3 classes.
The "outermost" class is a dispatcher (or router), which wraps the dispatch call inside a try-catch block. The dispatched request, calls a method in a third class, which runs code (wrapped in a try-catch block), which causes an exception.
Because I had omitted a use Exception; statement in the class where the error happens, the thrown exception trickles all the way back to the outermost layer (the dispatcher), where it is caught - causing me to scratch my head why the catch around the code causing the error isn't working.
To me this seems strange. Logically, PHP should in this situation (IMO) throw a Class not found exception/error, leading me to the actual error in my code, instead of trying to "stay alive" as long as possible.
Should this be filed as a bug, or is this expected behaviour?
Edit: Code example
File: class-a.php
<?php
namespace hello\world;
class classA {
protected $b;
public function __construct() {
$this->b = new \hello\world\classB();
}
public function doSomething() {
try {
$this->b->throwException();
} catch (Exception $e) {
}
}
}
File: class-b.php
<?php
namespace hello\world;
class classB
{
public function throwException() {
throw new \Exception("bar closed");
}
}
File: run.php
<?php
include 'class-a.php';
include 'class-b.php';
$a = new \hello\world\classA();
$a->doSomething();
ClassB throws an \Exception in ClassB::doSomething(), for which ClassA has a catch-clause, but because ClassA doesn't declare use Exception or catch (\Exception), the catch doesn't match and execution ends with a Uncaught exception error1. But in my opinion, it should cause a Class not found error.
I might be expecting too much of the permissive PHP compiler, but it would help in tracking down silly errors that should be easy for the compiler to spot.
1 If the $a->doSomething() in run.php was surrounded by a try..catch clause, the Exception would (or at least could) be caught there, since it trickles down the stack.
PHP's exception catching mechanism does not validate that the class you catch actually exists.
It exhibits the same behavior when using typehinting in functions, so I suspect it merely converts the exception/function type hint into a string or something and compares that with the type of the relevant object.
Whether this is a bug or not is questionable. Personally I think it should be classified as a bug, but PHP has all sorts of wonky behaviors :D

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