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 vari开发者_Go百科ous 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?
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.
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).
精彩评论