phpunit vermijd constructorargumenten voor mock

Wat is de manier om te voorkomen dat phpunit de constructor moet aanroepen voor een nepobject? Anders zou ik een nepobject nodig hebben als constructorargument, een ander daarvoor enz. De api lijkt zo te zijn:

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

Ik krijg het niet werkend. Het klaagt nog steeds over het constructorargument, zelfs met $callOriginalConstructoringesteld op false.

Ik heb maar één object in de constructor en het is een afhankelijkheidsinjectie. Dus ik denk niet dat ik daar een ontwerpprobleem heb.


Antwoord 1, autoriteit 100%

Je kunt getMockBuildergebruiken in plaats van alleen getMock:

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

Zie het gedeelte over “Test Doubles”in PHPUnit’s documentatievoor details.

Hoewel je dit kunt doen, is het veel beter om het niet te hoeven doen. U kunt uw code refactoren, dus in plaats van een concrete klasse (met een constructor) die moet worden geïnjecteerd, bent u alleen afhankelijk van een interface. Dit betekent dat je de interface kunt spotten of stompen zonder PHPUnit te vertellen om het gedrag van de constructor te wijzigen.


Antwoord 2, autoriteit 29%

Hier ga je:

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

Antwoord 3, autoriteit 5%

Deze vraag is een beetje oud, maar voor nieuwe bezoekers kun je deze doen met de createMock-methode (voorheen createTestDoublegenoemd en geïntroduceerd in v5.4.0).

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

Zoals je kunt zien in de onderstaande code die is geëxtraheerd uit de klasse PHPUnit\Framework\TestCase(in phpunit/src/framework/TestCase.php), het zal in feite een nep-object maken zonder de oorspronkelijke constructor aan te roepen.

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

Antwoord 4, autoriteit 3%

Als addendum wilde ik expects()-aanroepen koppelen aan mijn bespotte object en vervolgens de constructor aanroepen. In PHPUnit 3.7.14 is het object dat wordt geretourneerd wanneer u disableOriginalConstructor()aanroept, letterlijk een object.

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

Helaas is er in PHP 5.4 een nieuwe optie die ze niet gebruiken:

ReflectionClass::newInstanceWithoutConstructor

Omdat dit niet beschikbaar was, moest ik de klasse handmatig reflecteren en vervolgens de constructor aanroepen.

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

Let op, als functionCallFromConstructprotectedis, moet je specifiek setMethods()gebruiken zodat de beschermde methode wordt bespot. Voorbeeld:

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

setMethods()moet worden aangeroepen vóór de aanroep expect(). Persoonlijk koppel ik dit na disableOriginalConstructor()maar vóór getMock().


Antwoord 5

U kunt ook een parameter toevoegen aan getMockom het aanroepen van de standaardconstructor te voorkomen.

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

Toch vind ik het antwoord van dave1010 er mooier uitzien, dit is voor de volledigheid.


Antwoord 6

Misschien moet je een stub maken om door te geven als het constructorargument. Dan kun je die keten van nepobjecten doorbreken.


Antwoord 7

PHPUnit is ontworpen om de constructor aan te roepen op nagemaakte objecten; om dit te voorkomen moet u ofwel:

  1. Injecteer een schijnobject als afhankelijkheid in het object dat je moeilijk kunt spotten
  2. Maak een testklasse die de klasse uitbreidt die je probeert aan te roepen die niet de bovenliggende constructor aanroept

Other episodes