I understand the significance of the term 'fatal error', but I want to write a test class like this (disgustingly simplified):
class tester {
function execute() {
if( #$this->tryit() === true ) return true;
return false;
}
function tryit() {
$doesntexist = new noobject();
return true;
}
}
actually I'd have a Test parent class, and then classes would extend it and contain a bunch of methods with a bunch of tests. The parent class would define execute and it would just run every method in the child class (excluding execute of course) and collecting data on which functions pass and which fail.
I want to write tests before I actually write part of my code, but instead of using assert I just want to run every test and generate a list of which functions of which test classes fail. But that means if a test fails it means there was an error -- but I also want to handle instances where I forgot to define a class, etc. Is it possible to do that, while not having the entire script die?
I was thinking that the script would just fail up until the function call with the # in front of it, and then continue, but obviously I was wrong. Is there a workaround?
A fatal error is fatal, and there is nothing you can do about it.
Two ideas of solutions could be :
To test if the method exists before trying to call it ; see method_exists
Or, to run each "test" in a separate processus : this way, if there is a Fatal Error caused by one test, only the "child" process corresponding to that test dies, and the "parent" process, the test launcher, can detect this and consider it as a failure.
Actually, the second solution exists in PHPUnit since version 3.4, if I remember correctly ;-)
Fatal errors cannot be stopped, not even with set_error_handler. However, you can often find another way at the expense of writing more code. For the example method tryit, you can write an autoload function that triggers a non-fatal error or (in PHP 5.3.0) throws an exception, or use class_exists to skip the instantiation of a non-existent class.
Yes and No
You cannot write it so that the code picks up where it left off, after the fatal. However, you can use register_shutdown_function() to continue processing php after the fatal error.
I've written code that checks which kind of fatal error it was and then attempt to fix it before redirecting the user back to the same page, or dying.
register_shutdown_function is excellent for redirecting the user to a 500 error page with a contact form prevalued with the error info. This way I could have the users help me out by opening an issue on my github acct.
I'm guessing you would set up an error handler with the set_error_handler() function that calls into your testing class to report an error, but I'm not entirely sure exactly how you'd implement it.
With PHP 7, you can now try/catch a fatal error.
https://www.php.net/manual/en/language.exceptions.php
Related
Is it OK to perform a mysqli query on a class __destruct function?
I heard somewhere it will not always finish the query or it won't work as expected.
For example:
class stackoverflow{
function a() {
}
function b() {
}
function __destruct() {
//mysqli query here
}
}
Generally, you are better of creating your own method to carry out any work that needs to be done toward the end of the script and call it manually.
The principle reason for this in my view, is error handling.
According to the online documentation:
Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.
This means that should an error arise - for example - in your mysql query, a fatal error will shutdown your script.
a __destruct method may well be useful at times, but better used in situations that only require some simple hard-code, which is tested and not error prone.
Additionally, some servers may change various parameters during the shut-down phase. Such as the working directory.
There's no reason why it shouldn't work as expected. However, it would help if you explained why you wanted to do so in the first place, there may be a better solution than what you have in mind
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 use die() for error output in a class in PHP.
I sometimes call this class from another class. When the first class dies, I don't want it to kill the class that called it. I think a break() statement within a loop is a good analogy, but instead, I'm looking to break a class from within itself.
Is there a way to confine die() to the class that called it, or is there a preferred way to get this behavior?
Never die() in a class
You can use return, and return an error message, false, etc. Or you can throw an exception.
Galen is right. But I'd go further:
Never use die() for error reporting. Throw an exception - uncaught exceptions get sent to the PHP error log. Or use error_log in conjunction with a custom error/exception handler. Emit proper HTTP headers (500, 404, etc.). Show users a pretty error page.
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)
First of all, this question is purely theoretical. Fact is, whether it's possible or not, it would be terribly bad practice. Having said that, here's my question:
PHP offers the possibility to define custom error handlers via the set_error_handler function. By parsing the error message, it's possible to find out what triggered the error.
I'm interested mostly in 'Call to undefined function' errors. I know its possible to parse the error message to uncover the called function, and this got me thinking.
Would it be possible for the error handler, in case of an Undefined Function, to attempt to include a file (say functions.html.php for all functions starting with a html_ prefix), and then attempt to re-execute the function, plus arguments, that initially triggered the error? And, most importantly, return the function's value in case of success?
In brief without using exception handling you won't be able to recover from the error in the way you described.
There is a way to handle this specifically for undefined functions, however that is to say undefined member functions from an object. This is the __call() method. Basically if you call an undefined method from an object __call() then takes that function call and does whatever you put in the method body see http://php.net/manual/en/language.oop5.overloading.phplink text
It's not really possible to restart the execution where the error occured.
However: there is a system to loading classes on demand, using the __autoload function.
From the manual:-
You may define an __autoload function which is automatically called in case you are trying to use a class/interface which hasn't been defined yet. By calling this function the scripting engine is given a last chance to load the class before PHP fails with an error.
There is more in the PHP manual here: http://php.net/manual/en/language.oop5.autoload.php