95

What is the way to avoid phpunit having to call the constructor for a mock object? Otherwise I would need a mock object as constructor argument, another one for that etc. The api seems to be like this:

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

I don't get it to work. It still complains about the constructor argument, even with $callOriginalConstructor set to false.

I only have one object in the constructor and it is a dependency injection. So I don't think I have a design problem there.

yhw42
  • 3,334
  • 2
  • 27
  • 24

7 Answers7

156

You can use getMockBuilder instead of just getMock:

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

See the section on "Test Doubles" in PHPUnit's documentation for details.

Although you can do this, it's much better to not need to. You can refactor your code so instead of a concrete class (with a constructor) needing to be injected, you only depend upon an interface. This means you can mock or stub the interface without having to tell PHPUnit to modify the constructor behaviour.

dave1010
  • 15,135
  • 7
  • 67
  • 64
  • This works great for me. It should be example 10.3, though. I tried to edit the post but SO kicked it back for being too short an edit. – Matthew May 31 '12 at 13:34
43

Here you go:

    // 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
  • 754
  • 11
  • 28
  • This seems to be almost what I want. I want to call the getMock with only class to be mocked and the $callMockConstructor. How? something like this: $this->getMock($classToMock, $callMockConstructor). The only thing I could think of is to go in the source of PHPUnit and change it to default = false. – Gutzofter Apr 16 '10 at 18:53
  • 1
    I changed the default to false in testcase.php. You would think it would be set for false by default. Mocking a constructor seems very odd – Gutzofter Apr 16 '10 at 19:15
  • Excellent answer. Just what I was looking for – Hades Jan 18 '13 at 00:19
7

This question is a little old, but for new visitors, you can do it using the createMock method (previously called createTestDouble and introduced in v5.4.0).

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

As you can see in the code below extracted from the PHPUnit\Framework\TestCase class (in phpunit/src/framework/TestCase.php), it will basically create a mock object without calling the original constructor.

/** PHPUnit\Framework\TestCase::createMock method */
protected function createMock(string $originalClassName): MockObject
{
    return $this->getMockBuilder($originalClassName)
                ->disableOriginalConstructor()
                ->disableOriginalClone()
                ->disableArgumentCloning()
                ->disallowMockingUnknownTypes()
                ->getMock();
}
Wesley Gonçalves
  • 1,985
  • 2
  • 19
  • 22
5

As an addendum, I wanted to attach expects() calls to my mocked object and then call the constructor. In PHPUnit 3.7.14, the object that is returned when you call disableOriginalConstructor() is literally an 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)

Unfortunately, in PHP 5.4 there is a new option which they aren't using:

ReflectionClass::newInstanceWithoutConstructor

Since this wasn't available, I had to manually reflect the class and then invoke the constructor.

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

Note, if functionCallFromConstruct is protected, you specifically have to use setMethods() so that the protected method is mocked. Example:

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

setMethods() must be called before the expect() call. Personally, I chain this after disableOriginalConstructor() but before getMock().

Steve Tauber
  • 9,551
  • 5
  • 42
  • 46
2

Alternatively you could add a parameter to getMock to prevent the calling of the default constructor.

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

Still, I think the answer of dave1010 looks nicer, this is just for the sake of completeness.

William Desportes
  • 1,412
  • 1
  • 22
  • 31
Hans Wouters
  • 588
  • 5
  • 16
1

Perhaps you need to create a stub to pass in as the constructor argument. Then you can break that chain of mock objects.

Glenn Moss
  • 6,812
  • 6
  • 29
  • 23
0

PHPUnit is designed to call the constructor on mocked objects; to prevent this you should either:

  1. Inject a mock object as a dependency into the object you're having trouble mocking
  2. Create a test class that extends the class you're trying to call that doesn't call the parent constructor
silfreed
  • 1,243
  • 1
  • 9
  • 6