I have the following pseudo code
public function testSomething() {
// assert something
// assert something else
$this->setExpectedException(...);
// trigger my exception here
// do one last thing
}
The issue I see, code after the exception being triggered is never made. Is this correct?
This is just a general wondering here - if this is normal I will refactor my test to perform the try/catch directly and fail() the test if nothing is caught.
The code after the exception should not be made. Think of the setExpectedException as making the test into a try -- catch So the code after the exception is thrown will not be executed.
If you need to do/check things after the exception, you should catch it. Though one warning with your catch, be specific about what exception is being thrown. PHPUnit throws exceptions for failed tests and you could accidentally catch this exception which may result in your test falsely passing.
Update:
If the code that you are executing is cleaning up, consider also moving it into the tearDown method of the test.
Related
I'm writing a PHPUnit test (v4.6.7) for a new PHP class (v5.3.3) that does stuff with XML. The purpose of the test is to confirm that the class throws an DOMException when handed bad XML.
I want to collect the exception and perform an assertInstanceOf on it, comparing it to DOMException.
No matter what I do, by the time I can see the exception phpunit has already transformed the original exception into an instance of PHPUnit_Framework_Error_Warning.
For example, here is one effort in the code under test:
//...
try {
$this->code_that_causes_exception();
} catch (\Exception $exception) {
$className = get_class($exception);
throw $exception;
}
//...
When run from within phpunit, the string stored in $className is PHPUnit_Framework_Error_Warning, as opposed to whatever exception the code_that_causes_exception actually raised.
Begin Update
Just to clarify my question, with no try/catch around the code under test, the message reported by phpunit is DOMDocument::loadXML(): Start tag expected, '<' not found in Entity, line: 1. I make the perhaps rash assumption that DOMDocument is throwing some sort of exception to produce this message. The DOMDocument says that DOMException is the parent of exceptions thrown from DOMDocument objects.
End Update
I want the original exception so that I can wrap the code under test with an appropriate try/catch to gracefully handle this failure situation.
How do I obtain the original exception from which the PHPUnit_Framework_Error_Warning was instantiated?
Why this is happening
Your exception is not converted to a PHPUnit_Framework_Error_Warning, it is never thrown. PHPUnit automatically converts PHP Warnings into exceptions. This is enabled by the convertWarningsToExceptions property in the XML configuration file (there are also convertNoticesToExceptions and convertErrorsToExceptions respectively). All these settings are true by default.
This means that your original exception is probably never thrown, because a PHP warning is triggered before and then converted into an (instantly-thrown) exception by PHPUnit's error handler.
Have a closer look at your caught exception to see which statement in your code triggers the PHP warning (simply remove the try/catch statement to let PHPUnit present the error).
You can disable this behaviour in your phpunit.xml file:
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.6/phpunit.xsd"
...
convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false"
...
How you should test exceptions
The probably easiest way to assert that a specific exception is thrown is to use the #expectedException annotation. This is discussed in-depth in the manual. Using this annotation saves you a lof of boilerplate code:
/**
* #expectedException DOMException
* #test
*/
public function myTestCase() {
$this->code_that_causes_exception();
}
This test will pass when a DOMException is thrown and fail when no exception is thrown at all (or any other Exception is thrown).
I would like to know how throw works in PHP.
For example, does it act like a die() or exit()? How can I know what is done internally?
I am asking this because I saw Kohana using their $this->redirect() method with a throw to terminate the script execution instead of the traditional exit.
throw is not like exit or die at all. Throwing an exception does not automatically terminate the application, a thrown exception can be caught by the application. Only when an exception is not caught will the application be terminated.
try {
throw new Exception;
} catch (Exception $e) {
echo 'caught it';
}
echo 'not dead yet';
Exceptions are a mechanism to signal errors to higher up callers in a more flexible and rigorous manner than simple return false statements would allow. They are not comparable to a simple exit or die.
I don't know what Kohana does exactly, but throwing an exception instead of using a simple exit or die is an abuse of exceptions. Exceptions should be thrown in exceptional error circumstances only.
As already explained, you use throw to throw exceptions that can be caught "further up" in your application.
When you work with objects and object oriented programming you start coding every single object you make as a standalone object that you can give to someone else. The public methods of these are an API, and the phpdoc above each public method details what exceptions the class might throw under certain circumstances.
So, someone has created a standalone object that does something for you, like writing to a disk. You want to use this object, so you look at the docs and see it throws a PermissionsException when the object can't write to the disk because of a permissions issue.
In your code that uses this person's object, you now know that you should catch that exception, log it, and continue however you want your application to work given that circumstance (show a nice error to the user if it's via an AJAX call, for example).
So, knowing this, when you code your own objects, make descriptive exceptions for different circumstances that someone who you give your object to can use and respond to in their own applications.
Both die and exit you don't really want to use in production applications. They're useful for debugging when you do a var_dump() and then want to halt application execution straight afterwards or if you want to completely stop the script from running for some reason.
As for why your specific found piece of code does it this way, you should ask the developer if it isn't documented with good reasoning.
Using "throw" without try/catch will terminate the script with a "catchable fatal error". As far as I know there's no benefit in using "throw" this way. If you want to terminate a script you should use exit(), so you don't need to suppress the error message.
I have a class, which uses the Chain of Responsibility pattern. In a nutshell, under certain conditions, if one of its methods is called, it passes the call onto the same method of a successor (it and the successor share the same interface).
Example:
catch(PDOException $exception)
{
if(isset($this->successor))
{
$this->successor->log($log);
}
}
Basically, if the successor has been set, it is given the call. If not, nothing happens.
At the moment, in my Unit test, I have two tests: in one of them, the successor is set and the method is called under the right conditions to trigger the call to the successor. The successor is a mock and I can test that the right method is called.
Now, before writing the if statement, I duplicated the same test, but removed the code that sets the successor object. When I run the test, not surpisingly, the test stops with an error because the SUT is trying to call a method on an object that doesn't exist.
When I implement the code (like above) all the tests pass fine.
I am unhappy though. I follow TDD metholodogy and I would like to be able to see a failing test before I write code, not an error.
How can I refactor that so I can have a failing test first, instead of an error?
Sadly, this is unavoidable. PHP raises a fatal error (E_FATAL_ERROR) which kills the process. PHPUnit cannot intercept it to produce an error or failure, nor can your code.
$ php -r 'try { $o = null; $o->foo(); } catch (Exceptiion $e) { echo "fail"; }'
Fatal error: Call to a member function foo() on a non-object in Command line code on line 1
The most you can do is detect the error in a shutdown function and dump some diagnostic output. There is no way to recover from a fatal error. :(
As for TDD, I believe the goal is to write as much test code to get your tests to not pass. It may seem like a subtle distinction, but that means fail (F) or error (E) or die (stop running). Once you get there, write code to make the tests pass.
Place your unit tests into a TRY-CATCH-block. If you catch an exception you can convert it into an failure:
try {
...testing
} catch (Exception $e) {
$this->assertTrue(false, $e->getMessage());
}
I am getting my head around PHPUnit, and trying to build a test case for an existing class.
The class is a static configuration class, getting, setting and listing configuration options that will be available in the application.
The class is very strictly built. If I try to set a configuration setting with an incompatible value, or a configuration setting that does not exist, a E_USER_ERROR is thrown, halting the execution of the script. Even if it's not the fine art of error handling, it works fine for the purposes of this class. An error in that class is always the result of a programming error, and never of bad user input.
This has the great advantage that you don't have to worry about how error messages are handled or logged, which keeps the code slim. Output the message (or not, in production), die(), done.
However, with Unit tests, I don't see how I can continue to work with classic PHP errors. I will have to convert the functions to return success flags, or throw exceptions.
Am I correct?
Or is there a way in PHPUnit to expect errors, as there is to expect Exceptions? I can't see any straight away.
From the PHPUnit manual:
By default, PHPUnit converts PHP errors, warnings, and notices that are triggered during the execution of a test to an exception. Using these exceptions, you can, for instance, expect a test to trigger a PHP error as shown in Example 4.8.
class ExpectedErrorTest extends PHPUnit_Framework_TestCase
{
/**
* #expectedException PHPUnit_Framework_Error
*/
public function testFailingInclude()
{
include 'not_existing_file.php';
}
}
Same works for exceptions of course.
Edit: didn't see this was already mentioned in the comments, but I'll leave it here for reference in case someone looks for the same question and doesn't read the comments
If you want to cancel treating warning as Exception, use
PHPUnit_Framework_Error_Warning::$enabled=false; .
I don't know the details of your implementation, but couldn't you raise a normal (custom) exception (which can be tested) and have a global exception handler in your app that will throw the proper E_USER_ERROR based on these (custom) exceptions?
Check set_exception_handler for more information on setting this global exception handler.
Couldn't you define an error handler (via set_error_handler) that will treat any error that cannot be handled by PHPUnit (such as E_USER_ERROR) as a failure of the unit test?
(Inspired by WordPress' tests)
<?php
function some_function($arg) {
if($filter->check_for_safe_input($arg)) {
throw new Exception("Hacking Attempt");
}
do_some_database_stuff($arg);
}
?>
In the above code example, does do_some_database_stuff ever get called if check_for_safe_input fails, or does the exception stop the function running? It's something I've never quite been sure of, and usually I just stick functions like do_some_database_stuff in an else statement to be sure, but this tends to lead to hugely nested functions.
Yes, uncaught exceptions result in fatal errors that stop the execution of the script. So the do_some_database_stuff function will not be called if an exception is thrown. You can read more about exceptions in this article.
Have a look at the PHP manual on exceptions.
When an exception is thrown, code
following the statement will not be
executed, and PHP will attempt to find
the first matching catch block. If an
exception is not caught, a PHP Fatal
Error will be issued with an "Uncaught
Exception ..." message, unless a
handler has been defined with
set_exception_handler().
php.net/exceptions
So yes, the rest of the function is not being executed, a fatal error occurs instead.
If you catch the exception, execution of the script continues in the corresponding catch block, everything "between" the function which throws an exception and the catch block is not executed.
An exception, if not catched, will end script execution.
See the PHP manual chapter on Exceptions