2010-06-29 87 views
33

我很难用PHPUnit嘲笑PDO对象。使用PHPUnit嘲讽PDO对象

似乎没有成为我的问题在网络上但从我可以收集很多信息:

  1. PDO有“最终” __wakeup并防止它被序列化 __sleep方法。
  2. PHPunit的模拟对象实现在某个时间序列化对象。
  3. 当发生这种情况时,单元测试会失败并生成由PDO生成的PHP错误。

有旨在防止这种行为的一个特点,通过添加以下行到你的单元测试:

class MyTest extends PHPUnit_Framework_TestCase 

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

} 

来源: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....... 

任何PHPUnit的Pro的给我个忙吗?

感谢,

回答

47

$ backupGlobals不帮你,因为这个错误来自其他地方。 PHPUnit的3.5.2(可能更早版本)中拥有的PHPUnit /框架下面的代码/ 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'] 
     ) 
     ); 
    } 

这个“绝招”与当你问getMock不执行原来的构造反序列化使用它会立即失败,并与PDO。

那么,如何解决它呢?

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

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

} 

这里的目标是摆脱原来的PDO构造函数,它不需要的。然后,你的测试代码改成这样:

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

这样创建模拟将执行原来的构造,但因为现在这是无害的感谢mockPDO测试帮手,可以继续测试。

+0

这是做这个工作。谢谢! – uckelman 2010-11-04 19:29:03

+1

你是爸爸!非常感谢,这工作正常。我已经放弃了解决这个问题! – 2010-11-05 22:30:26

+0

我遇到了与原始海报相同的问题并使用了您的解决方案。不过,现在我的打印不再将其视为PDO。 '必须是PDO的一个实例,Mock_PDOMock_96936f72的实例给出' – nvanesch 2014-04-11 09:34:39

2

我能想到的是使用runkit并重新定义了两个最终方法为使用runkit_function_redefine保护最好的。

不要为了启用php.ini中的runkit.internal_override设置。

与以往一样,与EVAL,如果runkit似乎是一种解决的问题是,可能是错误的:)

+0

我不认为使用'runkit'或'eval'出现任何测试目的都是错误的。 – netcoder 2010-11-03 13:38:41

1

您正在您的测试用例中实例化您的测试用例吗?

$classToTest = new MyTest($pdoMock); 

现在,您基本上正在测试您的测试用例。它应该更类似于:

$classToTest = new My($pdoMock); 
+0

这绝对是原始问题中的一个错误。 – uckelman 2010-11-04 19:18:43