使用PHPUnit测试error_log

10

我有这个函数,我想测试一下它的样子:

class Logger {
  function error($msg){
    if (is_string($msg)){
      error_log($msg);
      die($msg);
    } elseif (is_object($msg)){
      error_log($msg.' '.$msg->getTraceAsString());
      die('exception');
    } else {
      var_dump($msg);
      die('error');
    }
  }

我想在不记录$msg的情况下测试此函数。有没有一种方法可以确定error_log是否有效而不记录日志?我尝试使用setExpectedException,但我无法捕获错误并且它仍然继续记录日志。


1
我的兴趣主要在于如何测试调用error_log的代码,Alister在当前命名空间中覆盖error_log的示例对于测试非常有效。 - ThorSummoner
3个回答

8
显而易见的答案是一个简单的别名/代理函数,它本身在Logger类中调用了error_log(可以轻松地进行模拟,并检查设置为它的内容)。
然而,要实际测试本机的error_log函数(原始类中没有代理),可以使用命名空间。测试将被定义为与原始代码相同的命名空间,然后在测试类之后添加一个函数 - 在这种情况下是error_log() - 但该函数也被定义在命名空间中 - 因此优先于来自本机函数的根命名空间等效项运行。
不幸的是,您无法像error_log那样覆盖die(或其别名exit)。它们是“语言结构”,不能像error_log一样被覆盖。
<?php
namespace abc;
use abc\Logger;

class ThreeTest extends \PHPUnit_Framework_TestCase
{
    public function setUp() { $this->l = new Logger(); }
    // test code to exercise 'abc\Logger'

}

// Now define a function, still inside the namespace '\abc'.
public function error_log($msg)
{
   // this will be called from abc\Logger::error
   // instead of the native error_log() function
   echo "ERR: $msg, ";
}

1
您可以使用函数模拟框架,例如php-mock(还有其他框架可用)来模拟对error_log的调用,并检查它是否使用了您期望的参数进行调用。
不幸的是,您将无法对die()构造使用该框架,因为它不是普通函数而是语言结构。
我会用'throw new \Exception()'(或任何其他适当的异常)替换die(),因为这样您就可以:
- 测试抛出的异常 - 在编程中决定在调用记录器时是否停止执行,或者是否要通过try/catch包装调用继续执行
但我也会问自己,在调用记录器时是否必须停止执行。

0

error_log()输出捕获到变量中

如果您想以一种可以使用PHPUnit断言进行检查的方式重定向error_log()输出,以下代码适用于我:

$errorLogTmpfile = tmpfile();
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
error_log("Test for this message");
ini_set('error_log', $errorLogLocationBackup);
$result = stream_get_contents($errorLogTmpfile);
// Result: [11-May-2022 22:27:08 UTC] Test for this message

正如您所看到的,它使用临时文件来收集输出,然后将内容抓取到变量中并重置error_log配置。

可重复使用的方法

个人而言,我将其组织成一对方法,并使用trait将其注入PHPUnit对象中,以便我可以重复使用它们。

当然,下面的代码不能直接使用,但它可以演示如何使此系统可重复使用:

trait WithWPTestCaseGeneralTools {
    
    var $gvErrorLogLocationBackup = "";
    var $gvErrorLogTmpfile = "";

    public function gvErrorLogStartListening() {
        
        $this->gvErrorLogTmpfile = tmpfile();
        $streamUri = stream_get_meta_data($this->gvErrorLogTmpfile)['uri'];
        $this->gvErrorLogLocationBackup = ini_set('error_log', $streamUri);
    }

    public function gvErrorLogGetContents() {
        
        ini_set('error_log', $this->gvErrorLogLocationBackup);      
        return stream_get_contents($this->gvErrorLogTmpfile);
    }
}

当然,你也可以使用一些使用全局变量的函数来实现相同的功能,如果这是你需要的,我会把它留给你!


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