如何用嘲笑来模拟内部通话

时间:2019-01-02 10:16:36

标签: laravel phpunit mockery

我尝试使用Mockery lib模拟我的服务方法。如果我从测试的上下文中调用该方法,它将起作用。但是,如果我从另一个方法调用它(例如,它从另一个经过测试的方法调用)-它从实现中返回原始数据,而不是从模拟返回。我做错了什么? 示例如下。

我添加了合同,因为我的实际实现使用它。我认为该问题与接口无关。

app / Contracts / TransactionsServiceContract.php

namespace App\Contracts;

interface TransactionsServiceContract
{
    public function getAllRequests(): array;

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

app / Services / 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();
    }
}

tests / 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
    }
}

libs / frameworks的版本:

  • Laravel:v5.7.19
  • PHPUnit:7.5.1
  • 模拟:1.2.0

感谢您的关注。新年快乐! :)

1 个答案:

答案 0 :(得分:0)

当您在\Mockery::mock(app()->make(TransactionsServiceContract::class))->makePartial();方法中调用setUp时,并没有真正替换应用容器中现有的实现。 Laravel的容器为您提供了bind方法(the documentation for that)。此外,您不会用模拟代替接口,因为接口对每个定义都不会做任何事情。
因此,实际上您会执行以下操作:

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

请注意,只有在您的代码通过注入或解析而不是通过调用new TransactionService来获取TransactionService实例的情况下,此方法才有效。