phpunit evita gli argomenti del costruttore per mock


85

Qual è il modo per evitare che phpunit debba chiamare il costruttore per un oggetto fittizio? Altrimenti avrei bisogno di un oggetto fittizio come argomento del costruttore, un altro per quello ecc. L'api sembra essere così:

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

Non riesco a farlo funzionare. Si lamenta ancora dell'argomento del costruttore, anche se è $callOriginalConstructorimpostato su false.

Ho solo un oggetto nel costruttore ed è un'iniezione di dipendenza. Quindi non penso di avere un problema di progettazione lì.

Risposte:


139

Puoi usare getMockBuilderinvece di solo getMock:

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

Vedere la sezione "Test Double" nella documentazione di PHPUnit per i dettagli.

Anche se puoi farlo, è molto meglio non averne bisogno. Puoi rifattorizzare il tuo codice così invece di una classe concreta (con un costruttore) che deve essere iniettata, dipendi solo da un'interfaccia. Ciò significa che puoi deridere o bloccare l'interfaccia senza dover dire a PHPUnit di modificare il comportamento del costruttore.


Questo funziona alla grande per me. Dovrebbe essere l'esempio 10.3, però. Ho provato a modificare il post ma COSÌ l'ho respinto perché era una modifica troppo breve.
Matteo

Grazie @Lytithwyn. Aggiornata la risposta.
dave1010

42

Ecco qui:

    // 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);

Questo sembra essere quasi quello che voglio. Voglio chiamare getMock con la sola classe da deridere e $ callMockConstructor. Come? qualcosa del genere: $ this-> getMock ($ classToMock, $ callMockConstructor). L'unica cosa a cui potrei pensare è andare nel sorgente di PHPUnit e cambiarlo in default = false.
Gutzofter

1
Ho cambiato l'impostazione predefinita in false in testcase.php. Penseresti che sarebbe impostato su false per impostazione predefinita. Prendere in giro un costruttore sembra molto strano
Gutzofter

Ottima risposta. Proprio quello che stavo cercando
Ade

4

Come aggiunta, volevo allegare expects()chiamate al mio oggetto deriso e quindi chiamare il costruttore. In PHPUnit 3.7.14, l'oggetto che viene restituito quando chiami disableOriginalConstructor()è letteralmente un oggetto.

// 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)

Sfortunatamente, in PHP 5.4 c'è una nuova opzione che non stanno usando:

ReflectionClass :: newInstanceWithoutConstructor

Poiché questo non era disponibile, ho dovuto riflettere manualmente la classe e quindi richiamare il costruttore.

$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);

Nota, se lo functionCallFromConstructè protected, devi specificatamente usare in setMethods()modo che il metodo protetto venga deriso. Esempio:

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

setMethods()deve essere chiamato prima della expect()chiamata. Personalmente, lo incateno dopo disableOriginalConstructor()ma prima getMock().


Non ho idea se questo sia un odore di codice, ma ha funzionato benissimo per me e volevo solo ringraziarti.
devbanana

1

Forse hai bisogno di creare uno stub da passare come argomento del costruttore. Quindi puoi spezzare quella catena di oggetti fittizi.


1

In alternativa è possibile aggiungere un parametro a getMock per impedire la chiamata del costruttore predefinito.

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

Tuttavia, penso che la risposta di dave1010 sia più bella, questo è solo per completezza.


1

Questa domanda è un po 'vecchia, ma per i nuovi visitatori, puoi farlo utilizzando il createMockmetodo (precedentemente chiamato createTestDoublee introdotto nella v5.4.0).

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

Come puoi vedere nel codice sottostante estratto dalla PHPUnit\Framework\TestCaseclasse (in phpunit/src/framework/TestCase.php), fondamentalmente creerà un oggetto fittizio senza chiamare il costruttore originale .

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

0

PHPUnit è progettato per chiamare il costruttore su oggetti fittizi; per evitare ciò dovresti:

  1. Inietta un oggetto fittizio come dipendenza nell'oggetto che hai problemi a deridere
  2. Crea una classe di test che estenda la classe che stai tentando di chiamare che non chiama il costruttore principale
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.