Mocking DI service

I am using Codeception with REST, Phalcon and Mockery modules. The issue is that replacing services with Mockery mocks doesn't seem to work. When I run the following code it still tries to use the real class instance instead of the mock:

    public function someTest(ApiTester $I)
    {
        $I->wantTo('test sth');

        $di = $I->getApplication()->getDI();
        $myServiceMock = Mockery::mock(MyService::class);
        $myServiceMock->shouldReceive('someMethod')
            ->andReturn(null);
        $myServiceMock->shouldReceive('setDI')
            ->andReturn($di);

        $I->haveServiceInDi('myService', function() use ($myServiceMock) {
            return $myServiceMock;
        }, true);

        $I->haveHttpHeader('Content-Type', 'application/json');
        $I->sendPOST('someUrl', []);
        $I->seeResponseCodeIs(HttpCode::OK);
        $I->seeResponseIsJson();
        $I->seeResponseContainsJson([
            'success' => true
        ]);
    }

Is there a better way to replace DI services with mock objects during runtime to make it work?

[I also tried AspectMock but it didn't work at all with Phalcon.]



1.1k

I am using PHPUnit, and have been mocking DI services like this. This pattern should work for you as well, if I am understanding your issue correctly. To mock out or replace a service in the DI, all you have to do is set it in the DI in your test.

Assume that SomeObject::run() internally requests myService from the DI.

    public function testSomething()
    {
        $this->getDI()->set('myService', function() {
            return new MockService();
        });

        $object = SomeObject();
        $output = $object->run(); // Internally requests myService from the DI, which will now return a MockService instance

        $this->assertEquals('output', $output);
    }
edited Sep '16

I am doing exactly the same. Just using Codeception's Phalcon module to set DI service instead of setting it directly like you do. My problem is that when I am using REST module (which uses Phalcon module in turn) by calling "sendPost()", DI services are reset to initial state. I can make it work by including the mocking code in the Phalcon module's bootstrap file but that's a pretty dirty solution.

There was a pretty much the same issue with Codeception's Laravel module described here, but the maintainer of Laravel module managed to fix it.