Как издеваться над внутренними звонками с издевательством - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь смоделировать метод моего сервиса с помощью 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();
    }
}

Тесты / Feature / 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
    }
}

Версии библиотек / каркасов:

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

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

1 Ответ

0 голосов
/ 07 января 2019

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

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

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

...