2010-06-29 8 views
33

Ich habe Schwierigkeiten, das PDO-Objekt mit PHPUnit verspotten.Mocking das PDO-Objekt mit PHPUnit

Es scheint nicht zu viele Informationen im Internet über mein Problem zu sein, aber von dem, was ich sammeln kann:

  1. PDO hat ‚final‘ __wakeup und __sleep Methoden, die es vor dem serialisiert verhindern.
  2. PHPunit's Mock-Objekt-Implementierung serialisiert das Objekt irgendwann.
  3. Die Komponententests schlagen dann mit einem PHP-Fehler fehl, der von PDO generiert wird, wenn dies auftritt.

Es gibt ein Merkmal dieses Verhalten gemeint ist, zu verhindern, indem Sie die folgende Zeile in der Unit-Test:

class MyTest extends PHPUnit_Framework_TestCase 

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

} 

Quelle: noch http://sebastian-bergmann.de/archives/797-Global-Variables-and-PHPUnit.html

Dies ist nicht für mich arbeiten, mein Test produziert ein Fehler.

Vollständiger Testcode:

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....... 

Alle PHPUnit Pro geben Sie mir eine Hand?

Danke,

Ben

Antwort

47

$ backupGlobals wird dir nicht helfen, weil dieser Fehler von anderswo kommt. PHPUnit 3.5.2 (möglicherweise frühere Versionen auch) hat den folgenden Code in 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'] 
     ) 
     ); 
    } 

Dieser "Trick" mit unserialize verwendet, wenn Sie getMock bitten, nicht den ursprünglichen Konstruktor ausführen und es wird sofort mit PDO scheitern.

Also, wie funktioniert es?

Eine Option ist ein Test Helfer wie diese

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

} 

Das Ziel ist die Schaffung des ursprünglichen PDO-Konstruktor, um loszuwerden, die Sie nicht brauchen. Ändern Sie dann Ihre Testcode dazu:

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

Erstellen von Mock wie diese wird ursprünglichen Konstruktor ausführen, aber da es nun harmlos dank mockPDO Test Helfer ist, können Sie Prüfung fortsetzen.

+0

Das macht den Job. Vielen Dank! – uckelman

+1

Du bist der Papa! Vielen Dank, das funktioniert gut. Ich hatte es aufgegeben, dieses Problem zu lösen! –

+0

Ich hatte das gleiche Problem wie das Original-Poster und nutzte Ihre Lösung. Aber jetzt sieht meine Typhinterlegung es nicht mehr als PDO. 'muss eine Instanz von PDO sein, Instanz von Mock_PDOMock_96936f72 gegeben ' – nvanesch

2

Das Beste, was ich von ist denken kann runkit und neu definieren die beiden letzten Methoden zu verwenden, wie mit runkit_function_redefine geschützt.

Nicht für die Einstellung runkit.internal_override in php.ini zu aktivieren.

Und wie eh und je, wie mit eval, wenn runkit wie die Antwort scheint, ist die Frage, wahrscheinlich falsch :)

+0

Ich glaube nicht, dass es etwas falsch ist mit 'runkit' oder' eval' zu Testzwecken. – netcoder

1

Sie instanziieren Ihren Testfall in Ihrem Testfall?

$classToTest = new MyTest($pdoMock); 

Im Moment testen Sie im Wesentlichen Ihren Testfall. Es sollte mehr etwas wie sein:

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

Das ist definitiv ein Fehler in der ursprünglichen Frage. – uckelman