【问题标题】:PHPunit - testing a custom error handling classPHPunit - 测试自定义错误处理类
【发布时间】:2020-07-17 22:52:43
【问题描述】:

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

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

这是一种有效的方法吗?

【问题讨论】:

    标签: php unit-testing phpunit


    【解决方案1】:

    无需测试 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 加载的自定义错误处理程序。
    • @stefgosselin - 我的意思是 PHP 将调用您使用 set_error_handler() 指定的方法,并且无需测试该方面。相反,编写测试以直接调用该方法并传入适当的参数。我将在我的答案中添加一个示例来说明。
    • 啊啊啊!我刚看到光!!谢谢大卫,这太棒了。
    • 如果PHPUnit is running,则仅调用set_error_handler()
    • @IanDunn 您必须确保在测试后将其重置为 PHPUnit 的处理程序,并且如果您的处理程序引发错误,仍然存在无限循环的风险。你最好不要测试 PHP 解释器本身。
    【解决方案2】:

    大卫回答的一个变体是只打电话给set_error_handler()if PHPUnit is not running。有时这更合适,具体取决于您的代码的架构方式、您构建的框架、向后兼容性等。

    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()。当error-handler.php被PHPUnit加载时,set_error_handler()不会被调用,所以你不用担心你的错误处理器会干扰到PHPUnit的错误处理器。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-08
      • 1970-01-01
      • 1970-01-01
      • 2017-06-08
      • 2020-05-06
      • 1970-01-01
      • 2013-02-12
      • 2011-01-29
      相关资源
      最近更新 更多