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;
}
}
Related
With PHP I am using the following class structure for exceptions:
class ProjectException extends Exception {
... logical code for handling exceptions, functions that are relevant to every exception my project throws go here
}
class SpecificException extends ProjectException {
protected $codePrefix = 'SE';
const ERROR_SOMETHING_BAD_HAPPENED = 100;
const ERROR_SOMETHING_SIMMILARLY_BAD_HAPPENED = 101;
/* no functions go in this class, this class is only to specify the code prefix of the exception, and the error codes used by it */
}
If I want to catch an exception, I usually use the following type of exception catching:
try {
/** try something here **/
} catch (SpecificException $e) {
switch ($e->getCode()) {
case SpecificException::ERROR_SOMETHING_BAD_HAPPENED:
/** react to the exception based on it's code, and try to recover **/
default:
/** react to every unexpected code, which may appear if the tried function is modified,
and throws a new exception code of the same type, and stop the execution of the
script, storing the error in the database **/
}
}
My problem is the following. I would like to "lock" some files from editing, for example some exception files. Why I would want to do this, is because I am trying to make part of my code portable, so that it can be used in other projects that we are working on also.
My idea was, that later on, in new projects, if something new comes up, and a new exception code should be made, I will extend the "locked" exception, and write the new error code as a const in the extending class. A real life scenario that could happen is the Account class. A bare Account class is already designed in the portable code, with login and register functionalities, but on each different project, these accounts will receive new functionalities which are project specific. Thus, in the new project, I will extend the portable projects Account class, and write the related functionalities there.
Theoretically, this could work, but I realized, that managing this later on will be a real pain I think, because:
when catching exceptions, I now either need to catch the parent exception, to catch every exception thrown, or I would have to write two (three, four, etc.) separate catch blocks, depending on how many times I extended the original parent exception
if I catch the parent exception, I can't just throw in all codes, without knowing the origin of the code (parent or child class)
Basically I would have to write simmilar try catch blocks to these:
try {
/** some code to try **/
} catch (SpecificException $e) {
/** this will catch everything that is SpecificException or SpecificExtendedException or SpecificDoubleExtendedException **/
switch ($e->getCode()) {
case SpecificException::ERROR_IN_SPECIFIC:
/** do something **/
break;
case SpecificException::ERROR_TWO_IN_SPECIC:
/** do something **/
break;
case SpecificDoubleExtendedException::ERROR_IN_SECOND_EXTENDED_EXCEPTION:
/** do something **/
break;
default:
/** as it was originally written **/
break;
}
}
Any one has any idea on how should I proceed? What would be the most natural way to "lock" some portable files, and add new Exception codes to the exceptions that are defined in those "locked" files?
You could simply add a method like this in your ProjectException class:
public function getCodeFor(string $name): int
{
return (int)constant("static::ERROR_$name");
}
This uses the constant helper function along with the static keyword to retrieve a constant defined in the actual class of the object that method was called on.
Then you could have your try block this way, which would work for any class extending SpecificException:
switch ($e->getCode()) {
case $e->getCodeFor('SOMETHING_BAD_HAPPENED'):
/** do something **/
break;
case $e->getCodeFor('SOMETHING_SIMILARLY_BAD_HAPPENED'):
/** do something **/
break;
case $e->getCodeFor('SOMETHING_SPECIFICALLY_BAD_HAPPENED'):
/** do something **/
break;
}
Demo: https://3v4l.org/sAQdE
Note: obviously, using strings makes you lose the IDE refactoring capability of your constants. But since you can't know in advance which classes will define a given code (or you can, but would like not to have to), then this is a reasonable workaround.
Note 2: there might be better ways to do things design-wise (and as #Barmar pointed out, asking Software Engineering might be more appropriate for architectural talk). This answer is merely attempting to provide a programming solution to your current issue, without altering said design.
Assuming the following example code:
/**
* #method bool someMethod()
*/
class MyClass
{
/**
* #throws MyClassException
*/
public function __call($method, $args)
{
if ($this->someCheck()) {
throw new MyClassException();
}
}
}
//...
try {
(new MyClass())->someMethod();
} catch (MyClassException $e) { // Reported by PHPStorm as not thrown!
// of course the exception is properly caught
}
How can I make IDE detect exceptions thrown by a methods declared with #method docblock? Wonder if this is even possible to do, if not - what are my alternatives?
It seems like #throws declared in magic methods are totally ignored in cases like this. Of course I could disable inspections but this isn't clean solution for me...
It says it was possible for some time (some 2018.1.x versions if I'm reading the ticket correctly) but then it was rolled back in 2018.1.3 "due to usability concerns".
I agree with that -- not everyone will be happy to see unhandled exception warnings for every magic method call (e.g. Laravel uses that a lot) -- simply because not every magical method can throw exceptions.
Anyway: https://youtrack.jetbrains.com/issue/WI-39284 -- watch this ticket (star/vote/comment) to get notified on any progress.
Update 2021
It does not seem to work in the latest PhpStorm. Syntax was also removed from PSR draft, so this is probably no longer a viable solution.
Original answer
You can document exceptions for magic methods in this way:
/**
* MyClass summary.
*
* #method bool someMethod() {
* #throws MyClassException
* }
*/
class MyClass {
// ...
}
This syntax is part of PSR-5 draft. Standard is still not accepted, but it seems to work pretty well in PhpStorm.
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.
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
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.