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

发布于 2024-11-08 20:02:27 字数 1100 浏览 2 评论 0原文

我想测试我组合在一起的自定义错误处理类,但这本身就带来了一些问题。

首先,由于在测试中设置自定义 error_handler 会覆盖 PHPUnits 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 ();

这是一种有效的方法吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

软糖 2024-11-15 20:02:27

无需测试 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 一遍又一遍地调用您的处理程序,直到解释器因堆栈溢出或内存不足而终止。

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.

撩人痒 2024-11-15 20:02:27

David 的答案的一个变体是仅调用 set_error_handler() 如果 PHPUnit 没有运行。有时这是更合适的,具体取决于您的代码的架构方式、您构建的框架、向后兼容性等。

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

然后,tests/ test-error-handler.php 可以像测试任何其他函数一样测试 foo() 。当 PHPUnit 加载 error-handler.php 时,set_error_handler() 将不会被调用,因此您不必担心您的错误处理程序会干扰 PHPUnit 的错误处理程序。

核心思想仍然是相同的:在包含要测试的文件时,您不希望出现任何副作用(PSR 1.2.3)。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文