Как издеваться над внутренними вызовами с помощью mockery

Я пытаюсь издеваться над методом моего обслуживания с помощью Mockery lib. Это работает, если я вызываю этот метод из контекста теста. Но если я вызываю его из другого метода (например, он вызывает из другого проверенного метода) — он возвращает исходные данные из реализации, а не из мока. Что я делаю неправильно? Пример ниже.

Я добавил контракт, потому что моя реальная реализация использует его. Я не думаю, что проблема связана с интерфейсами.

приложение/Контракты/TransactionsServiceContract.php

namespace App\Contracts;

interface TransactionsServiceContract
{
    public function getAllRequests(): array;

    public function getRequests(array $necessaryFields): array;
}

приложение/Услуги/TransactionsService.php

namespace App\Services;

use App\Contracts\TransactionsServiceContract;

class TransactionsService implements TransactionsServiceContract
{

    public function getAllRequests(): array
    {
        return [
            'foo' => [
                'metric' => 'foo',
            ],
            'bar' => [
                'metric' => 'bar',
            ],
            'another' => [
                'metric' => [
                    // Some fields
                ],
            ],
        ];
    }

    public function getRequests(array $necessaryFields): array
    {
        // dd($this->getAllRequests()); // -> for the test context it returns original value (above's one)
        return collect($this->getAllRequests())->only($necessaryFields)
            ->map(function (array $metric) {
                return $metric['formula'];
            })
            ->toArray();
    }
}

тесты/Функция/TransactionsServiceTest.php

namespace Tests\Feature;

use App\Contracts\TransactionsServiceContract;
use Tests\TestCase;

class TransactionsServiceTest extends TestCase
{
    /** @var TransactionsServiceContract */
    private $_transactionsService;

    public function setUp()
    {
        parent::setUp();
        $requests = [
            'test1' => [
                'metric' => 'test 1',
            ],
            'test2' => [
                'metric' => 'test 2',
            ],
        ];
        $this->_transactionsService = \Mockery::mock(app()->make(TransactionsServiceContract::class))->makePartial();
        $this->_transactionsService->shouldReceive('getAllRequests')->andReturn($requests);
    }

    public function testInternalCall()
    {
        $directCall = $this->_transactionsService->getAllRequests(); // returns array "requests" from the setUp method
        dump($directCall);
        $internalCall = $this->_transactionsService->getRequests(['test1']);
        dd($internalCall); // if we call getAllRequests into getRequests, but not from test's context, we get original array from real implementation, but not test's mock
    }
}

Версии библиотек/фреймворков:

  • Ларавель: v5.7.19
  • PHPUnit: 7.5.1
  • Издевательство: 1.2.0

Спасибо за внимание. С Новым Годом! :)


person Pavel Savushkin    schedule 02.01.2019    source источник


Ответы (1)


Когда вы вызываете \Mockery::mock(app()->make(TransactionsServiceContract::class))->makePartial(); в своем методе setUp, вы на самом деле не заменяете реализацию, существующую в контейнере приложения. Контейнер Laravel предоставляет вам метод привязки для этого (документация для этого) . Кроме того, вы не стали бы заменять интерфейс макетом, поскольку интерфейсы ничего не делают по определению.
Таким образом, на самом деле вы должны сделать что-то вроде:

app()->bind('\App\TransactionsService', $mockedTransactionService);

Обратите внимание, что это будет работать только в том случае, если ваш код получает экземпляр TransactionService путем внедрения или разрешения, а не путем вызова new TransactionService.

person Borisu    schedule 07.01.2019
comment
спасибо за Ваш ответ! Другими словами, я не могу издеваться над методами класса, которые будут возвращены из DI, верно? Итак, мне нужно создать экземпляр $mockedTransactionService (через новый), имитировать методы, а затем привязать его к моему контракту? Ну да. Это имеет смысл, потому что тестовый файл создается именно для тестирования реализации. Спасибо за вашу помощь! - person Pavel Savushkin; 09.01.2019