phpunit vermeide Konstruktorargumente für mock

85

Wie kann vermieden werden, dass phpunit den Konstruktor für ein Scheinobjekt aufrufen muss? Andernfalls würde ich ein Scheinobjekt als Konstruktorargument benötigen, ein anderes dafür usw. Die API scheint so zu sein:

getMock($className, $methods = array(), array $arguments = array(),
        $mockClassName = '', $callOriginalConstructor = TRUE,
        $callOriginalClone = TRUE, $callAutoload = TRUE)

Ich bekomme es nicht zum Laufen. Es beschwert sich immer noch über das Konstruktorargument, selbst wenn $callOriginalConstructores auf false gesetzt ist.

Ich habe nur ein Objekt im Konstruktor und es ist eine Abhängigkeitsinjektion. Ich glaube also nicht, dass ich dort ein Designproblem habe.

yhw42
quelle

Antworten:

138

Sie können getMockBuilderanstelle von nur verwenden getMock:

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

Weitere Informationen finden Sie im Abschnitt "Test Doubles" in der PHPUnit-Dokumentation .

Obwohl Sie dies tun können, ist es viel besser, nicht zu müssen. Sie können Ihren Code so umgestalten, dass anstelle einer konkreten Klasse (mit einem Konstruktor), die eingefügt werden muss, nur eine Schnittstelle erforderlich ist. Dies bedeutet, dass Sie die Schnittstelle verspotten oder stubben können, ohne PHPUnit anweisen zu müssen, das Konstruktorverhalten zu ändern.

dave1010
quelle
Das funktioniert gut für mich. Es sollte jedoch Beispiel 10.3 sein. Ich habe versucht, den Beitrag zu bearbeiten, aber SO hat er zurückgeworfen, weil er zu kurz bearbeitet wurde.
Matthew
Danke @Lytithwyn. Die Antwort wurde aktualisiert.
Dave1010
42

Bitte schön:

    // Get a Mock Soap Client object to work with.
    $classToMock = 'SoapClient';
    $methodsToMock = array('__getFunctions');
    $mockConstructorParams = array('fake wsdl url', array());
    $mockClassName = 'MyMockSoapClient';
    $callMockConstructor = false;
    $mockSoapClient = $this->getMock($classToMock,
                                     $methodsToMock,
                                     $mockConstructorParams,
                                     $mockClassName,
                                     $callMockConstructor);
Matthew Purdon
quelle
Das scheint fast das zu sein, was ich will. Ich möchte das getMock mit nur zu verspottender Klasse und dem $ callMockConstructor aufrufen. Wie? so etwas wie: $ this-> getMock ($ classToMock, $ callMockConstructor). Das einzige, woran ich denken könnte, ist, in die Quelle von PHPUnit zu gehen und sie in default = false zu ändern.
Gutzofter
1
Ich habe die Standardeinstellung in testcase.php auf false geändert. Sie würden denken, dass es standardmäßig auf false gesetzt ist. Einen Konstruktor zu
verspotten
Hervorragende Antwort. Genau das, wonach ich gesucht habe
Hades
4

Als Ergänzung wollte ich expects()Aufrufe an mein verspottetes Objekt anhängen und dann den Konstruktor aufrufen. In PHPUnit 3.7.14 ist das Objekt, das beim Aufruf zurückgegeben disableOriginalConstructor()wird, buchstäblich ein Objekt.

// Use a trick to create a new object of a class
// without invoking its constructor.
$object = unserialize(
sprintf('O:%d:"%s":0:{}', strlen($className), $className)

Leider gibt es in PHP 5.4 eine neue Option, die sie nicht verwenden:

ReflectionClass :: newInstanceWithoutConstructor

Da dies nicht verfügbar war, musste ich die Klasse manuell widerspiegeln und dann den Konstruktor aufrufen.

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

$mock->expect($this->once())
    ->method('functionCallFromConstructor')
    ->with($this->equalTo('someValue'));

$reflectedClass = new ReflectionClass('class_name');
$constructor = $reflectedClass->getConstructor();
$constructor->invoke($mock);

Beachten Sie , wenn functionCallFromConstructist protected, müssen Sie speziell verwenden , setMethods()so dass die geschützte Methode verspottet wird. Beispiel:

    $mock->setMethods(array('functionCallFromConstructor'));

setMethods()muss vor dem expect()Anruf angerufen werden. Persönlich verkette ich das nach, disableOriginalConstructor()aber vorher getMock().

Steve Tauber
quelle
Keine Ahnung, ob dies ein Code-Geruch ist, aber das hat bei mir großartig funktioniert und ich wollte mich nur bei Ihnen bedanken.
Devbanana
1

Möglicherweise müssen Sie einen Stub erstellen, der als Konstruktorargument übergeben wird. Dann können Sie diese Kette von Scheinobjekten durchbrechen.

Glenn Moss
quelle
1

Alternativ können Sie getMock einen Parameter hinzufügen , um den Aufruf des Standardkonstruktors zu verhindern.

$mock = $this->getMock(class_name, methods = array(), args = array(), 
        mockClassName = '', callOriginalConstructor = FALSE);

Trotzdem denke ich, dass die Antwort von dave1010 besser aussieht, nur der Vollständigkeit halber.

Hans Wouters
quelle
1

Diese Frage ist etwas alt, aber für neue Besucher können Sie sie mit der createMockMethode createTestDoubleausführen (die zuvor in Version 5.4.0 aufgerufen und eingeführt wurde).

$mock = $this->createMock($className);

Wie Sie im folgenden Code sehen können, der aus der PHPUnit\Framework\TestCaseKlasse (in phpunit/src/framework/TestCase.php) extrahiert wurde , wird im Grunde genommen ein Scheinobjekt erstellt, ohne den ursprünglichen Konstruktor aufzurufen .

/** PHPUnit\Framework\TestCase::createMock method */
protected function createMock(string $originalClassName): MockObject
{
    return $this->getMockBuilder($originalClassName)
                ->disableOriginalConstructor()
                ->disableOriginalClone()
                ->disableArgumentCloning()
                ->disallowMockingUnknownTypes()
                ->getMock();
}
Wesley Gonçalves
quelle
0

PHPUnit dient zum Aufrufen des Konstruktors für verspottete Objekte. Um dies zu verhindern, sollten Sie entweder:

  1. Fügen Sie ein Scheinobjekt als Abhängigkeit in das Objekt ein, für das Sie Probleme beim Verspotten haben
  2. Erstellen Sie eine Testklasse, die die Klasse, die Sie aufrufen möchten, erweitert und den übergeordneten Konstruktor nicht aufruft
silfreed
quelle