Phpunit: 不允许对 'Closure' 进行序列化

创建于 2012-01-02  ·  12评论  ·  资料来源: sebastianbergmann/phpunit

尽管$GLOBAL闭包自 01aa34783c9b19d39e568641f3f2dd0fc983b11a & #352 起不再序列化,但包含闭包作为值的$GLOBAL数组仍会导致“不允许'闭包'的序列化”错误。

$GLOBALS[] = array( 'foo' => function() { return 'bar'; }  );
typbug

最有用的评论

每当实例化new MyRestClient()时,我都会$this->client = new \GuzzleHttp\Client(); 。 当我尝试在我的类的构造函数中使用$x=function(){};时也是如此。 我猜 Guzzle 客户端在内部做了类似的事情。 这使得不可能从 phpunit 测试中实例化。 我现在好难过:sob:

所有12条评论

另一个案例我得到它

 /* 测试.php */
类closureTest 扩展了 PHPUnit_Framework_TestCase
 {
 函数 testClosure() {
 $this->assertEquals(1, 1);
 call_user_func(function($a) {
 throw new \Exception("test");
 }, 10);
 $this->assertEquals(2, 2);
 }
 }

然后在进程隔离中运行它

 phpunit --process-isolation test.php

你会得到

有1个错误:
 1) phTest::testClosure
 PHPUnit_Framework_Exception:PHP 致命错误:未捕获的异常“异常”,消息为 -:37 中的“不允许对 'Closure' 进行序列化”
堆栈跟踪:
 #0 -(37):序列化(数组)
 #1 -(123): __phpunit_run_isolated_test()
 #2 {主要}
投入-在第37行
失败!
测试:1,断言:0,错误:1。

我在使用进程隔离时遇到了同样的问题,为了解决这个问题,我将 __sleep 添加到我的 testCase 并清理了那里的闭包。 对我来说,我在测试用例的属性中存储了一些模拟,所以这真的是我自己的错,我希望这对某人有所帮助。

我也遇到同样的问题,可以复制前面提到的问题@tmilos 。 我不知道这张票是否已经死了,但如果我们能解决这个问题就好了。 如果愿意,我更愿意提出拉取请求。

拉取请求总是受到赞赏!

我知道这有点过了,但我有一段时间不在项目中。 除了@tmilos的评论

/* test.php */
class closureTest extends PHPUnit_Framework_TestCase
{
    function testClosure() {
        call_user_func(function($a) {
            throw new \Exception("test");
        });
    }
}

请注意call_user_func末尾缺少提供的参数。 序列化异常是由尝试对PHPUnit_Framework_TestResult对象进行序列化导致的。 这个序列化尝试可以在PHPUnit/Framework/Process/TestCaseMethod.tpl.dist 42 行找到。 要被序列化的对象的违规部分是抛出的异常的跟踪层次结构中的闭包引用:

[1] => Array
    (
        [function] => {closure}
        [class] => Issue451Test
        [type] => ->
        [args] => Array
            (
            )

)

我的问题是,因为这是我第一次修补 PHPUnit,所以没有剥离闭包或将其修改为良性的 - 这无疑会涉及使用反射 - 是否有一些 PHPUnit 最佳实践方法来处理这个问题? 至少有任何想法或意见吗?

另外,我在 Regressions/GitHub 下写了一个测试。 这是可以接受的吗?

@kunjalpopat ,关于您的问题,我要检查的第一件事是例外情况。 当处于进程隔离状态时,抛出的异常可能会给您一条“关闭序列化”消息。 尝试将您的测试代码包装在 try/catch 中,看看是否解决了问题,如果确实如此,那么您可能需要考虑削减 try/catch 的覆盖范围,直到找到问题区域。

问题在于PHPUnit_Util_GlobalState::backupGlobals

if ($key != 'GLOBALS' &&
    !in_array($key, $superGlobalArrays) &&
    !in_array($key, $blacklist) &&
    !$GLOBALS[$key] instanceof Closure) {  // <-- this is the problem
    self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]);
}

我在PHPUnit_Util_GlobalState编写了一些帮助程序:

public static function checkIfThereIsClosureInIt($arr) {
    if ($arr instanceof Closure)
        return true;
    if (is_object($arr))
        $arr = get_object_vars($arr);
    if (is_array($arr))
        foreach ($arr as $x)
            if (PHPUnit_Util_GlobalState::checkIfThereIsClosureInIt($x))
                return true;
    return false;
}

并更改了一点backupGlobals函数:

foreach (array_keys($GLOBALS) as $key) {
    if ($key != 'GLOBALS' &&
        !in_array($key, $superGlobalArrays) &&
        !in_array($key, $blacklist) &&
        !PHPUnit_Util_GlobalState::checkIfThereIsClosureInIt($GLOBALS[$key])
//        !$GLOBALS[$key] instanceof Closure
    ) {
        self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]);
    }
}

这个版本似乎正在运作(我再也没有发现这个例外)。
由于它是对 $GLOBALS 的递归,因此它可以为 cpu 提供一些额外的热量。

来自 stafr 的上述代码修复修复了我在“闭包序列化”异常中遇到的 PHPunit 问题。 非常感谢!

每当实例化new MyRestClient()时,我都会$this->client = new \GuzzleHttp\Client(); 。 当我尝试在我的类的构造函数中使用$x=function(){};时也是如此。 我猜 Guzzle 客户端在内部做了类似的事情。 这使得不可能从 phpunit 测试中实例化。 我现在好难过:sob:

只是补充一下我的经验。 基本上,每当您将匿名函数或类作为测试类属性时,phpunit都将抛出Serialization of 'Closure' is not allowedSerialization of 'class<strong i="6">@anonymous</strong>' is not allowed 。 因此,如果您需要使用匿名函数或类,请确保它是一次性变量,而不是测试类属性。

此页面是否有帮助?
0 / 5 - 0 等级