mockery: Partial mocks do not mock constructor

After recent changes partial mocking of constructor doesn’t work.

namespace Acme;

class ExampleClass
{
    public function __construct(DependencyInterface $dep)
    {
    }
}

\Mockery::mock('Acme\ExampleClass[__construct]');

After running phpunit following error occurs:

Argument 1 passed to 'Mockery_518b69580914a::__construct() must implement interface Acme\DependencyInterface, none given

About this issue

  • Original URL
  • State: closed
  • Created 11 years ago
  • Comments: 26

Most upvoted comments

I’m not sure it’s possible not to call constructor while creating a partial mock at all in PHP 5.3 (this is described in Mockery docs too). In PHP 5.4 there is a new method (http://www.php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php), that allows for such behavior.

But in your case I would have created a mock (without expectations) in setUp method and given all required constructor parameters there. Then in each test added needed expectation and all would have worked. Or created a helper function that creates a mock with only given method mocked.

Also if you have an error in constructor of a class you’re testing, then it’s you who will be fixing it. I see no problem in fact, that this error pops up in every test. It still needs to be fixed.

When I’m creating a unit test, I have a separate test method for each method of my class. To ensure that I’m only testing that one method, I create a partial mock, with stubs for all the methods except the one I’m testing:

class Widget
{
    public function getHalfFoo()
    {
        $foo = $this->getFoo();
        return $foo / 2;
    }

    public function getFoo()
    {
        // fetch integer from somewhere and return it
        return $result;
    }
}

In my test class, I have something like this (though I use a helper method to do it consistently):

class WidgetTest extends UnitTestCase
{
    public function testGetHalfFoo()
    {
        $widget = m::mock('Widget[getFoo]')
            ->shouldReceive('getFoo')
                ->andReturn(8)
            ->getMock();

        $this->assertSame(4, $widget->getHalfFoo());
    }
}

By structuring my tests in this way, I can make sure that each test method is only testing the correct method. If I just let the getFoo() method pass through to the real implementation, now my test depends on the behaviour of that method too. I want to only test that whatever is returned from getFoo() is then halved by getHalfFoo().

After #144, this isn’t possible if the constructor has required arguments. Having required arguments allows the object to guarantee that it has a dependency it needs. If the constructor has required arguments, and there are none provided, I really think Mockery shouldn’t be calling the constructor without arguments and throwing an error.

This is a weird example as this Widget class might be poorly architected but it illustrates the use case.

I guess we can’t please everyone, I’m not too sure what the best course of action is, this isn’t something I tend to run in to.

I don’t think Mockery should force this type of mock to have or not have its constructor called, I think it should be optional. Passing an empty array should call the constructor, passing null or omitting the second argument should skip the constructor call.

I’m not very familiar with the internals of Mockery (and all the use cases that need to be accounted for), so I wasn’t keen to get my hands dirty and submit a pull request for this. But I might take a look if I’m the only one who wants this 😄