【问题标题】:How to mock internal calls with mockery如何用嘲讽来模拟内部调用
【发布时间】:2019-05-28 23:50:50
【问题描述】:

我尝试使用 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();
    }
}

测试/功能/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

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

【问题讨论】:

    标签: laravel phpunit mockery


    【解决方案1】:

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

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

    请注意,这仅在您的代码通过注入或解析而不是通过调用 new TransactionService 获取 TransactionService 实例时才有效。

    【讨论】:

    • 感谢您的回答!换句话说,我不能模拟将从 DI 返回的类的方法,对吗?那么,我需要创建一个实例 $mockedTransactionService(通过 new)、模拟方法,然后将其绑定到我的合同吗?嗯,是。这是有道理的,因为创建测试文件是为了准确测试实现。感谢您的帮助!
    猜你喜欢
    • 2015-01-07
    • 2017-11-20
    • 2019-06-12
    • 2016-01-08
    • 2014-11-01
    • 2015-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多