PHPunit - 测试自定义错误处理类

3
我想测试我编写的自定义错误处理类,但这本身就存在一些问题。
首先,由于在测试中设置自定义error_handler会覆盖PHPUnit的error_handling,因此每次更改为自定义实现并进行测试时,必须调用restore_error_handler(),以便后续的测试可以使用phpunits error_handling。虽然我承认没有尝试过,也许PHPUnit会在每个测试之后重置自己,但我有点怀疑。
我有各种配置要测试,在不同条件下,即E_ERROR,E_WARNING等。
然后我想到了模拟error_handler,但我对测试的模拟方面仍然很陌生。通过快速的谷歌搜索,我发现了一个类似的方法,这里是修改后并以一种可能适合我的需要的方式实施的想法...
//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 ();

这是一种有效的方法吗?

2个回答

10

没有必要测试 PHP 的 set_error_handler() 函数。你只需要关心你的错误处理类是否有效即可。将 PHP 解释器的测试留给 PHP 开发者。 :) 将你的类视为其他任何类:实例化它并调用其方法,传递参数以测试不同情况。

例如,假设你的错误处理程序看起来像这样:

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);
        }
        ...
    }
}

您可以使用一个模拟日志记录器来测试上面的这个功能,而不需要调用set_error_handler()或触发实际的PHP错误。相反,只需像PHP在这些情况下所做的那样调用您的错误处理程序:

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');
}

美妙的是,如果您的错误处理程序由于一个漏洞触发了PHP错误,PHPUnit将捕获它并使测试失败。如果您用自己的处理程序替换PHPUnit的处理程序,很可能会进入无限循环,因为PHP会一遍又一遍地调用您的处理程序,直到解释器由于堆栈溢出或内存不足而死亡。


我试图测试的不是set_error_handler本身,而是由set_error_handler加载的自定义错误处理程序。 - Stephane Gosselin
3
我的意思是,PHP会使用set_error_handler()调用您指定的方法,因此无需测试该方面。相反,编写测试以直接调用该方法并传入适当的参数。我将在答案中添加示例以说明。 - David Harkness
啊!我刚看到了光明!!谢谢David,这太棒了。 - Stephane Gosselin
一个变体是仅在运行PHPUnit测试时调用set_error_handler() - Ian Dunn
这不是我想表达的意思。我会添加一个答案,以便我可以更充分地解释它。 - Ian Dunn
显示剩余2条评论

1
David的答案有一个变化,就是只在PHPUnit未运行时调用set_error_handler()根据你的代码架构、构建的框架、向后兼容性等因素,有时这更适合。

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://dev59.com/3mkv5IYBdhLWcg3w71H7
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;
}

然后,tests/test-error-handler.php 可以像测试其他函数一样测试 foo()。当 PHPUnit 加载 error-handler.php 时,set_error_handler() 不会被调用,因此您不必担心您的错误处理程序会干扰 PHPUnit 的错误处理程序。
核心思想仍然相同:在包含要测试的文件(PSR 1.2.3)时,不希望出现任何副作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接