@runInSeparateProcess时反序列化错误

Cha*_*rry 6 php phpunit

我正在实现一个模块,它将提供一个API来处理和管理PHP会话.我正在测试Session\Manager允许用户启动会话,设置ID,获取ID,销毁会话等的实现.我正在使用PHPUnit的@runInSeparateProcess注释在单独的过程中测试此类中的方法.当我使用这个注释时,由于反序列化错误,我得到PHPUnit抛出的异常.当我不使用注释时,测试按预期运行,并且在null不等于false时失败.

这是导致错误的测试.到目前为止还没有实现详细信息,接口的所有方法都存在但不执行任何操作.

class ManagerTest extends PHPUnitTestCase {

    /**
     * Ensures that if the session has not been started yet the sessionExists
     * method returns false.
     *
     * @runInSeparateProcess
     */
    public function testSessionExistsWhenSessionHasNotBeenStarted() {
        $Manager = new \Session\Manager();
        $this->assertFalse($Manager->sessionExists());
    }

}
Run Code Online (Sandbox Code Playgroud)

我能够将问题追溯到以下PHPUnit_Util_PHP::runJob()方法.我正在运行PHPUnit 3.7.5并且runJob被调用的方法是:

/**
 * Runs a single job (PHP code) using a separate PHP process.
 *
 * @param  string                       $job
 * @param  PHPUnit_Framework_TestCase   $test
 * @param  PHPUnit_Framework_TestResult $result
 * @return array|null
 * @throws PHPUnit_Framework_Exception
 */
public function runJob($job, PHPUnit_Framework_Test $test = NULL, PHPUnit_Framework_TestResult $result = NULL)
{
    $process = proc_open(
      $this->getPhpBinary(),
      array(
        0 => array('pipe', 'r'),
        1 => array('pipe', 'w'),
        2 => array('pipe', 'w')
      ),
      $pipes
    );

    if (!is_resource($process)) {
        throw new PHPUnit_Framework_Exception(
          'Unable to create process for process isolation.'
        );
    }

    if ($result !== NULL) {
        $result->startTest($test);
    }

    $this->process($pipes[0], $job);
    fclose($pipes[0]);

    $stdout = stream_get_contents($pipes[1]);
    fclose($pipes[1]);

    $stderr = stream_get_contents($pipes[2]);
    fclose($pipes[2]);

    proc_close($process);
    $this->cleanup();

    if ($result !== NULL) {
        $this->processChildResult($test, $result, $stdout, $stderr);
    } else {
        return array('stdout' => $stdout, 'stderr' => $stderr);
    }
}
Run Code Online (Sandbox Code Playgroud)

该行$stdout = stream_get_contents($pipes[1]);导致$stdout等于长串?.在$this->processChildResult该值$stdout是反序列化,并传递给这个函数无效值触发警告,导致抛出异常.我也能够确定返回值$this->getPhpBinary()/usr/bin/php.

抛出异常消息:

PHPUnit_Framework_Exception: ???...???"*???...??


Caused by
ErrorException: unserialize(): Error at offset 0 of 10081 bytes
Run Code Online (Sandbox Code Playgroud)

感谢hek2mgl,$job可以在这个gist上查看PHP代码,并在$ job上保存var_dump的输出.我创建了一个链接,因为它是一个相当数量的代码,这个问题已经很长了.

我已经达到了我对这个特定领域的了解,并且不确定如何进一步调试这个问题.我不确定为什么@runInSeparateProcess失败以及为什么$stdout运行一个单独的进程会产生一长串?分数.可能导致此问题的原因是什么?我该如何解决它?我对此模块处于停滞状态,因为未来的测试需要在单独的进程中运行,以确保启动和销毁会话不会影响测试.

  • PHP:5.3.15
  • PHPUnit:3.7.5
  • IDE和命令行测试运行程序中导致的错误

Cha*_*rry 8

好吧,我弄清楚这里发生了什么,男孩是不是很糟糕.

在测试期间,框架试图保持全局状态.在PHPUnit_Framework_TestCase::run()函数中,当前的全局变量被转换为PHP代码字符串PHPUnit_Util_GlobalState::getGlobalsAsString().我目前正在使用自动加载库来处理需要适当的类文件.此类在全局范围内的变量中声明,导致该对象的序列化被放入$GLOBALS由创建的数组中getGlobalsAsString().无论出于何种原因,此序列化包括空字节字符\u0000.当您尝试反序列化时,您会收到错误,并最终导致输出也被破坏.

我通过在我的测试引导程序中创建一个函数来修复此问题,该函数在全局范围之外创建自动加载对象.现在脚本不再尝试反序列化空字节,脚本不会产生错误,并且测试用例按预期工作.这是一个非常时髦的问题,不确定我的代码中是否有错误,PHPUnit的代码或serialize函数的内部错误.


或者,您可以覆盖run()测试用例中的函数,并显式设置测试用例以保留全局状态.这可能是一个更简洁的解决方案,因为您可以轻松地遇到其他问题,再次要求所有包含的文件,这是默认行为.

class ManagerTest extends PHPUnit_Framework_TestCase {

    public function run(PHPUnit_Framework_TestResult $result = null) {
        $this->setPreserveGlobalState(false);
        parent::run($result);
    }

}
Run Code Online (Sandbox Code Playgroud)

全球状态必须设置之前PHPUnit_Framework_TestCase::run()被调用函数.这可能是正确可靠地设置全局状态保存的唯一方法.