使用PHPUnit模拟PDO对象

Ben*_*ine 33 php phpunit pdo unit-testing

我在使用PHPUnit模拟PDO对象时遇到困难.

网上似乎没有太多关于我的问题的信息,但是我可以收集的信息:

  1. PDO具有'final'__ wakeup和__sleep方法,可以防止它被序列化.
  2. PHPunit的模拟对象实现在某些时候将对象序列化.
  3. 当发生这种情况时,单元测试会因PDO生成的PHP错误而失败.

通过在单元测试中添加以下行,有一个旨在防止此行为的功能:

class MyTest extends PHPUnit_Framework_TestCase

{    
    protected $backupGlobals = FALSE;
     // ...

}
Run Code Online (Sandbox Code Playgroud)

资料来源:http://sebastian-bergmann.de/archives/797-Global-Variables-and-PHPUnit.html

这不适合我,我的测试仍然会产生错误.

完整的测试代码:

class MyTest extends PHPUnit_Framework_TestCase
{

    /**
     * @var MyTest
     */
    private $MyTestr;

    protected $backupGlobals = FALSE;

    /**
     * Prepares the environment before running a test.
     */
    protected function setUp()
    {
        parent::setUp();

    }

    /**
     * Cleans up the environment after running a test.
     */
    protected function tearDown()
    {

        parent::tearDown();
    }

    public function __construct()
    {

        $this->backupGlobals = false;
        parent::__construct();

    }


    /**
     * Tests MyTest->__construct()
     */
    public function test__construct()
    {

        $pdoMock = $this->getMock('PDO', array('prepare'), array(), '', false);

        $classToTest = new MyTest($pdoMock);

        // Assert stuff here!


    }

    // More test code.......
Run Code Online (Sandbox Code Playgroud)

任何PHPUnit专业版都能帮到我手中?

谢谢,

Ant*_*nna 47

$ backupGlobals对你没有帮助,因为这个错误来自其他地方.PHPUnit 3.5.2(也可能是早期版本)在PHPUnit/Framework/MockObject/Generator.php中具有以下代码

    if ($callOriginalConstructor &&
        !interface_exists($originalClassName, $callAutoload)) {
        if (count($arguments) == 0) {
            $mockObject = new $mock['mockClassName'];
        } else {
            $mockClass  = new ReflectionClass($mock['mockClassName']);
            $mockObject = $mockClass->newInstanceArgs($arguments);
        }
    } else {
        // Use a trick to create a new object of a class
        // without invoking its constructor.
        $mockObject = unserialize(
          sprintf(
            'O:%d:"%s":0:{}',
            strlen($mock['mockClassName']), $mock['mockClassName']
          )
        );
    }
Run Code Online (Sandbox Code Playgroud)

当你要求getMock不执行原始构造函数时,会使用带有反序列化的"技巧",并且它会立即失败并使用PDO.

那么,如何解决呢?

一种选择是创建这样的测试助手

class mockPDO extends PDO
{
    public function __construct ()
    {}

}
Run Code Online (Sandbox Code Playgroud)

这里的目标是摆脱你不需要的原始PDO构造函数.然后,将您的测试代码更改为:

$pdoMock = $this->getMock('mockPDO', array('prepare'));
Run Code Online (Sandbox Code Playgroud)

像这样创建模拟执行原始构造函数,但由于mockPDO测试助手现在它是无害的,您可以继续测试.