【问题标题】:Issue with Mockery when injecting multiple mocked objects into controller将多个模拟对象注入控制器时出现模拟问题
【发布时间】:2017-06-11 08:23:15
【问题描述】:

我在基于 Laravel 的 PHP 项目中使用 Mockery 来帮助测试 Laravel MVC 控制器。下面是我正在尝试测试的控制器类的相关部分。

class DevicesController extends Controller
{
    private $deviceModel;
    private $rfDeviceModel;
    private $userModel;
    private $userDeviceModel;

    public function __construct(Device $deviceModel, RFDevice $rfDeviceModel, User $userModel, UserDevice $userDeviceModel)
    {
        $this->middleware('guest');

        $this->deviceModel = $deviceModel;
        $this->rfDeviceModel = $rfDeviceModel;
        $this->userModel = $userModel;
        $this->userDeviceModel = $userDeviceModel;
    }

    ...

    public function add(Request $request)
    {
        $name = $request->input('name');
        $description = $request->input('description');
        $onCode = $request->input('onCode');
        $offCode = $request->input('offCode');
        $pulseLength = $request->input('pulseLength');
        $type = 1;

        $currentUserId = $this->currentUser()->id;

        $newDeviceId = $this->deviceModel->add($name, $description, $type)->id;
        $this->rfDeviceModel->add($onCode, $offCode, $pulseLength, $newDeviceId);
        $this->userDeviceModel->add($currentUserId, $newDeviceId);

        return redirect()->route('devices');
    }
}

特别是,我围绕控制器的add(Request $request) 函数编写了几个单元测试,以确保调用三个模型add(...) 函数中的每一个。我处理这个问题的测试用例如下所示:

public function testAdd_CallsAddForModels()
{
    $mockDeviceModel = Mockery::mock(Device::class);
    $mockDeviceModel->shouldReceive('add')->withAnyArgs()->once();
    $this->app->instance(Device::class, $mockDeviceModel);

    $mockRFDeviceModel = Mockery::mock(RFDevice::class);
    $mockRFDeviceModel->shouldReceive('add')->withAnyArgs()->once();
    $this->app->instance(RFDevice::class, $mockRFDeviceModel);

    $mockUserDeviceModel = Mockery::mock(UserDevice::class);
    $mockUserDeviceModel->shouldReceive('add')->withAnyArgs()->once();
    $this->app->instance(UserDevice::class, $mockUserDeviceModel);

    $user = $this->givenSingleUserExists();

    $this->addDeviceForUser($user->user_id);
}

private function givenSingleUserExists()
{
    $user = new User;

    $name = self::$faker->name();
    $email = self::$faker->email();
    $userId = self::$faker->uuid();

    $user = $user->add($name, $email, $userId);

    return $user;
}

private function addDeviceForUser($userId)
{
    $this->withSession([env('SESSION_USER_ID') => $userId])
        ->call('POST', '/devices/add', [
        'name' => 'Taylor',
        'description' => 'abcd',
        'onCode' => 1,
        'offCode' => 2,
        'pulseLength' => 3
    ]);
}

当我运行这个测试时,我在控制台中得到以下输出:

There was 1 error:

1) Tests\Unit\Controller\DeviceControllerTest::testAdd_CallsAddForModels
Mockery\Exception\InvalidCountException: Method add() from Mockery_1_App_RFDevice should be called
 exactly 1 times but called 0 times.

但有趣和令人困惑的是,如果我注释掉并组合 3 个嘲弄部分中的 2 个,我的测试通过了。这意味着,我的代码实际上工作正常,但由于某种原因,在这种情况下,我无法将多个模拟模型对象注入我的控制器并一次测试它们。我我可以将其拆分为三个单独的测试,以确保调用每个模型的add(...) 函数,但如果可能的话,我想在一个测试用例中完成所有操作。我也知道我可以使用存储库模式将控制器的 add(...) 函数中的所有业务逻辑包装到一个调用中,但是在测试存储库类时我会遇到同样的问题。

【问题讨论】:

  • 尝试在 setUp 中绑定 mock,而不是在 test 函数中。
  • 我试过你的建议,我得到了完全相同的行为。我认为我不希望在 setUp 中绑定我的模拟,因为我不需要为我的大多数测试用例共享这些模拟。
  • 路由是怎么定义的?
  • 我只是尝试复制您的代码并使用mockery/mockery 0.9.7laravel/framework 5.3.30 以及5.4.4 运行相同的测试。模拟被正确注入并且测试通过没有问题。你用的是什么版本的 Mockery 和 Laravel?
  • 我降级到您的确切版本,一切仍然有效。这是代码github.com/nCrazed/SO-41863723

标签: php laravel phpunit mockery


【解决方案1】:

您不是在模拟方法的返回值,因此此行尝试访问 null 上的属性 (id)。

   $newDeviceId = $this->deviceModel->add($name, $description, $type)->id;

您可以通过向 Device 模型模拟添加返回值来解决此问题,如下所示:

$mockDeviceModel = Mockery::mock(Device::class);
$device = new Device;
$mockDeviceModel->shouldReceive('add')->withAnyArgs()->once()->andReturn($device);

为了使此类问题在未来更易于调试,请更改错误处理程序以在测试环境中重新引发异常,而不是呈现有效的 HTML 响应。

【讨论】:

    猜你喜欢
    • 2020-05-19
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    • 2012-01-26
    • 1970-01-01
    相关资源
    最近更新 更多