3

I want to test a custom error handling class I put together but this poses a few issues in itself.

First off, since setting a custom error_handler in a test overrides PHPUnits error_handling , one has to call restore_error_handler() every time it is changed to custom implementation and tested, for any later tests to have phpunits error_handling. Though I admit not having tried maybe PHPUnit does reset itself after every test I kind of doubt it.

I have various configurations to test, in different conditions ie E_ERROR , E_WARNING etc ..

I then had the idea of mocking the error_handler, but am still quite green on mocking aspects of testing. A quick google search made me find a similar approach, here is the idea modified and implemented in a way that may suit my needs ...

//Create mock with error_handler method
$errorH = $this->getMock ('ErrorHandler', array ('error_handler'));

// error handler should be called at least once
$errorH->expects ($this->atLeastOnce())->method ('error_handler');

// Mock will  need to be configured (here?) and tested 
// against specific error_handling settings 
//  not implemented yet nor needed for this post.

//set mock as error handler for duration of this test
set_error_handler (array($errorH, 'error_handler'));

//test current mock error handler configuration 
// for different conditions-> NOTICE / WARNING / ERROR

// Give phpunit's toy back!
restore_error_handler ();

Is this a valid approach?

2 Answers 2

10

There's no need to test PHP's set_error_handler() function. You only care that your error handling class works. Leave testing the PHP interpreter to the PHP developers. :) Treat your class like any other class: instantiate it and call its methods, passing parameters to test different conditions.

For example, let's say your error handler looked something like this:

class ErrorHandler
{
    private $logger;

    private $mode;

    public function __construct($mode, $logger) {
        $this->mode = $mode;
        $this->logger = $logger;
    }

    public function handleError($code, $message, $file='', $line=0, $context=array()) {
        if ($code = E_WARNING && $this->mode == 'dev') {
            // warnings are logged in development mode
            $this->logger->log($message, Logger::WARN);
        }
        ...
    }
}

You can use a mock logger to test the one feature above without calling set_error_handler() or triggering an actual PHP error. Instead, make the call to your error handler just as PHP would do under those conditions:

function testWarningsAreLoggedInDevelopment() {
    $logger = $this->getMock('Logger', array('log'));
    $logger->expects($this->once())
           ->method('log')
           ->with('message', Logger::WARN);
    $handler = new ErrorHandler('dev', $logger);
    $handler->handleError(E_WARNING, 'message');
}

The beauty is that if your error handler triggers a PHP error due to a bug, PHPUnit will catch it and fail the test. If you replace PHPUnit's handler with your own, you're likely to enter an infinite loop as PHP calls your handler over and over again until the interpreter dies from a stack overflow or runs out of memory.

7
  • I was trying to test not set_error_handler itself but the custom error handler that is loaded by set_error_handler. Commented May 19, 2011 at 23:00
  • 3
    @stefgosselin - What I mean is that PHP will call the method you specify using set_error_handler(), and there's no need to test that aspect. Instead, write your tests to call that method directly and pass in appropriate arguments. I'll add a sample to my answer to illustrate. Commented May 20, 2011 at 1:44
  • Aaaah! I just saw the light!! Thanks David, this is just awesome. Commented May 20, 2011 at 3:28
  • A variation on this is to only call set_error_handler() if PHPUnit is running.
    – Ian Dunn
    Commented Jul 16, 2020 at 1:19
  • @IanDunn You'd have to make sure to reset it to PHPUnit's handler after the test and still risk an infinite loop if your handler raises an error. You're better off not testing the PHP interpreter itself. Commented Jul 17, 2020 at 6:22
1

A variation on David's answer is to only call set_error_handler() if PHPUnit is not running. Sometimes this is a better fit, depending on how your code is architected, the framework you're building on top of, backwards compatibility, etc.

error-handler.php:

// This constant is defined in your `phpunit.xml`, and can be named anything. There are several other ways to detect it as well, see https://stackoverflow.com/questions/10253240/how-to-determine-if-phpunit-tests-are-running
if ( ! defined( 'RUNNING_PHPUNIT_TESTS' ) || ! RUNNING_PHPUNIT_TESTS ) {
    set_error_handler( 'handle_error' );
}

function handle_error( $error_code, $error_message, $file, $line ) {
    if ( foo() ) {
        // stuff...
    }

    return false;
}

function foo() {
    return $whatever;
}

Then, tests/test-error-handler.php can just test foo() like you would any other function. When error-handler.php is loaded by PHPUnit, set_error_handler() will not be called, so you don't have to worry about your error handler interfering with PHPUnit's error handler.

The core idea is still the same: you don't want any side effects when including the file that you're going to test (PSR 1.2.3).

Not the answer you're looking for? Browse other questions tagged or ask your own question.