Symfony 3.3 service mocks for functional tests
Asked Answered
V

1

9

Before Symfony 3.3 it was allowed to set a mocked service onto the container. Now with 3.3 a deprecation warning is thrown because the service is already pre-defined.

What is the new standard way to overwrite an existing or pre-defined service in the container to set a mocked service for a functional test?

E.g. in our case we set a new entity manager with a new mocked connection pointing to a cloned database for testing.

$container->set('doctrine.orm.entity_manager', $testEm);

Setting the "doctrine.orm.entity_manager" pre-defined service is deprecated since Symfony 3.3 and won't be supported anymore in Symfony 4.0.

Ventilate answered 2/6, 2017 at 7:20 Comment(4)
Why you need to mock entity_manager for testing purpose, when you can just change test environment configuration?Gemmiparous
That is not enough for us. We have various database functional tests which each receive a new cloned database to work on. So mocking the entity manager must be possible for each test class or even test method.Ventilate
use fixtures and setUp() and tearDown() etc, there are easier and better ways of going about this, instead of actually 'cloning' into an actual seperate databaseKizzykjersti
The EntityManager is just one example, we simply have the need to mock a deeply injected service, for that we need another way. If no other way exists, we are going to extend the Container and set our mocked services manually.Ventilate
G
2

I had the very same problem just a few days ago and I wrote a library to trick Symfony's DIC: https://github.com/TiMESPLiNTER/proxy-mock

The idea is to override the service in the config_test.yml with a "proxy" from the original service class which redirects all the calls to a mock which then can be set dynamically in the test case.

# config_test.yml
services:
    timesplinter.proxy_mock.factory:
        class: timesplinter\ProxyMock\ProxyMockFactory

    acme.api.client:
        factory: 'timesplinter.proxy_mock.factory:create'
        arguments: ['Acme\AppBunde\Services\ApiClient']

This will override the service defined in the original service.(xml|yml) with a proxy of it.

In the test case you can then do something like this:

// Your container aware test case (this exmaple is for PHPUnit)
$mock = $this->getMockBuilder(ApiClient::class)->disableOriginalConstructor()->getMock();

$container->set('acme.api.client')->setMock($mock);

With this your test will run against the mock you provide using the setMock() method.

The library is very new so some feature may be missing. If you use it and miss something please provide a pull request with the desired feature.

Grishilde answered 6/7, 2017 at 12:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.