使用 PHPUnit 测试 error_log
testing error_log with PHPUnit
我有这个功能,我想测试一下:
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
但我无法捕获错误并且它一直在记录。
显而易见的答案是一个简单的 alias/proxy-function,它本身在 Logger class 中调用 error_log
(可以很容易地模拟,并检查它的设置),
然而,要实际测试本机 error_log 函数(在原始 class 中没有代理),可以使用名称空间来完成。测试最终会定义为与原始代码相同的命名空间,然后在测试 class 之后添加一个函数 - 在本例中为 error_log()
- 但该函数也在命名空间中定义 -因此 运行 优先于本机函数的根命名空间等价物。
不幸的是,你不能用die
(或其别名exit
)做同样的覆盖。它们是 'language constructs',不能像 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, ";
}
您可以使用像 php-mock 这样的函数模拟框架(还有其他框架)来模拟对 error_log 的调用(并检查它是否使用您期望的参数调用)。
不幸的是,您将无法将其用于 die-construct,因为它不是普通函数,而是一种语言结构。
我会将 die() 替换为 'throw new \Exception()'(或任何其他适当的异常),然后
- 测试抛出的异常和
- 可以在您的编程中决定是在调用记录器时停止执行,还是通过将调用包装到 try/catch
中来继续执行
但我也会问自己在调用记录器时是否必须停止执行
在变量中捕获 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 配置。
Re-usable 方法
就个人而言,我将其组织成一对方法,我将这些方法注入到具有特征的 PHPUnit 对象中,这样我就可以 re-use 它们。
当然下面的代码不能开箱即用,但它可以演示如何制作这个系统re-usable:
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);
}
}
您当然可以使用几个使用全局变量的函数来实现相同的目的,如果您需要的话,我会把它留给您!
我有这个功能,我想测试一下:
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
但我无法捕获错误并且它一直在记录。
显而易见的答案是一个简单的 alias/proxy-function,它本身在 Logger class 中调用 error_log
(可以很容易地模拟,并检查它的设置),
然而,要实际测试本机 error_log 函数(在原始 class 中没有代理),可以使用名称空间来完成。测试最终会定义为与原始代码相同的命名空间,然后在测试 class 之后添加一个函数 - 在本例中为 error_log()
- 但该函数也在命名空间中定义 -因此 运行 优先于本机函数的根命名空间等价物。
不幸的是,你不能用die
(或其别名exit
)做同样的覆盖。它们是 'language constructs',不能像 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, ";
}
您可以使用像 php-mock 这样的函数模拟框架(还有其他框架)来模拟对 error_log 的调用(并检查它是否使用您期望的参数调用)。
不幸的是,您将无法将其用于 die-construct,因为它不是普通函数,而是一种语言结构。
我会将 die() 替换为 'throw new \Exception()'(或任何其他适当的异常),然后
- 测试抛出的异常和
- 可以在您的编程中决定是在调用记录器时停止执行,还是通过将调用包装到 try/catch 中来继续执行
但我也会问自己在调用记录器时是否必须停止执行
在变量中捕获 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 配置。
Re-usable 方法
就个人而言,我将其组织成一对方法,我将这些方法注入到具有特征的 PHPUnit 对象中,这样我就可以 re-use 它们。
当然下面的代码不能开箱即用,但它可以演示如何制作这个系统re-usable:
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);
}
}
您当然可以使用几个使用全局变量的函数来实现相同的目的,如果您需要的话,我会把它留给您!