Как смоделировать сервис с Symfony 4 в функциональных тестах? - PullRequest
0 голосов
/ 29 мая 2018

У меня есть обработчик командной шины, который внедряет некоторые службы:

class SomeHandler
{
    private $service;

    public function __construct(SomeService $service)
    {
        $this->service = $service;
    }

    public test(CommandTest $command)
    {
        $this->service->doSomeStuff();
    }
}

В SomeService есть метод doSomeStuff с внешними вызовами, который я не хочу использовать во время тестирования.

class SomeService
{
    private $someBindedVariable;

    public function __construct($someBindedVariable)
    {
        $this->someBindedVariable = $someBindedVariable;
    }

    public function doSomeStuff()
    {
        //TODO: some stuff
    }
}

Тамнаходится в тесте, я пытаюсь заменить службу на фиктивный объект

public function testTest()
{
    $someService = $this->getMockBuilder(SomeService::class)->getMock();
    $this->getContainer()->set(SomeService::class, $someService);

    //TODO: functional test for the route, which uses SomeHandler
}

Первая проблема заключается в том, что этот код вызывает исключение "Служба" App \ Service \ SomeService "является частной, вы не можете заменить ее."

Хорошо, давайте попробуем сделать это общедоступным:

services.yaml:

App\Service\SomeService:
    public: true
    arguments:
        $someBindedVariable: 200

Но это не помогает.Я получаю ответ от родного SomeService.Давайте попробуем с псевдонимами:

some_service:
    class: App\Service\SomeService
    public: true
    arguments:
        $someBindedVariable: 200

App\Service\SomeService:
    alias: some_service

И снова фиктивный объект не использует тест.Я вижу ответ от собственного SomeService.

Я попытался добавить опцию autowire, но это не помогло.

Что я должен сделать, чтобы заменить SomeService каким-либо фиктивным объектом по всему проекту во время теста

Ответы [ 2 ]

0 голосов
/ 06 ноября 2018

У меня почти такая же проблема с издевательством.Но в моем случае мне нужно смоделировать ApiClient, чтобы избежать реальных вызовов API в тестовой среде, и мне нужно сделать эту настройку настраиваемой, чтобы иметь возможность смоделировать любой запрос / ответ для каждого теста в режиме реального времени илидобавить несколько пар запрос / ответ, так как в некоторых случаях требуется несколько вызовов API для разных конечных точек для одного функционального теста.То, как это было раньше, было очень легко реализовать и прочитать:

 $apiClientMock = \Mockery::mock(HttpClientInterface::class);
 $apiClientMock
            ->shouldReceive('send')
            ->with($request)/            
            ->andReturn(new Response(HttpCode::OK, [], '{"data":"some data"}'))
            ->once();

 $I->replaceSymfonyServiceWithMock($apiClientMock, HttpClientInterface::class);

, поэтому в приведенном выше коде я могу добавить столько реализаций, сколько захочу для каждого теста.Но сейчас в Symfony не работает.Я не знаю, почему они не могут сделать контейнер для тестовой среды, как они сделали здесь https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing, но также и с возможностью устанавливать сервисы в реальном времени (я думаю, что есть причина, по которой они не могут, я 'я не очень знаком с этим).

Я могу создать другую реализацию сервиса и добавить ее в services_test.yml (этот файл конфигурации читает во время тестов, поэтому вам не нужно передавать имя класса в качестве параметра env) и использовать его втестирование env, но я все еще не могу добавить к нему ожидания для каждого теста, я могу лишь жестко закодировать все ожидания в этой реализации, но его грязное решение, как для меня, и простая задача, состоящая в том, что создание макета становится настоящей головной болью для васнеобходимо создать много кода, просто чтобы заменить некоторые функциональные возможности класса, и это усложняет процесс создания тестов, но я думаю, что это должно быть просто

0 голосов
/ 29 мая 2018

Лучший способ сделать это - явно определить этот сервис и позволить ему быть параметрическим, чтобы вводимый класс мог основываться на параметрах среды (поэтому, в основном, определяем другую реализацию для теста и разработчика сервиса).вы пытаетесь ввести).

По существу, что-то вроде

<service id="yourService">
  <argument type="service" id="%parametric.fully.qualified.class.name%" />
</service>

Затем вы можете определить в common.yaml FQCN для реальной реализации

parameters:
    parametric.fully.qualified.class.name: Fully\Qualified\Class\Name\Real\Impl

и в test.yaml (который должен быть загружен после common.yaml, чтобы переопределить его) реализация заглушки

parameters:
    parametric.fully.qualified.class.name: Fully\Qualified\Class\Name\Stub\Impl

И все готово, не касаясь какой-либо строки кода илилюбая строка тестового файла: просто конфигурация.

Обратите внимание

Это только концептуальный и иллюстративный пример, вы должны адаптировать его к своему коду (см. common.yaml и test.yaml дляэкземпляр) и имена ваших классов (см. yourService и все вещи FQCN)

Более того, в Symfony 4 в качестве файлов конфигурации можно заменить файл .env, чтобы получить тот же результат, выпросто пнеобходимо адаптировать эти понятия.

...