Мокинг в тестах контроллера с привязкой к модели Laravel

Я использую привязку модели в своих маршрутах для передачи моделей в действия моего контроллера и хотел бы иметь возможность писать тесты. Было бы предпочтительнее, если бы не требовалось, чтобы тест попадал в базу данных.

В этом примере модель связывается с использованием имени пользователя, а затем используется в определении маршрутов.

// routes.php
Route::model('user', function($value, $route)
{
    return User::whereUsername($value)->firstOrFail();
});

Route::get('users/{user}', 'UsersController@show');

В моем контроллере связанный пользователь передается действию.

// UsersController.php
function show(User $user)
{
    return View::make('users.show', compact('user');
}

Теперь в своих тестах я пытаюсь издеваться над пользователем.

// UsersControllerTest.php
public function setUp()
{
    parent::setUp();

    $this->mock = Mockery::mock('Eloquent', 'User');
    $this->app->instance('User', $this->mock);
}

public function testShowPage()
{
    $this->mock->shouldReceive('whereSlug')->once()->andReturn($this->mock);

    $this->action('GET', 'UsersController@show');

    $this->assertResponseOk();
    $this->assertViewHas('user');
}

При запуске этого теста я получаю следующую ошибку:

ErrorException: Argument 1 passed to UsersController::show() must be an instance of User, instance of Illuminate\Database\Eloquent\Builder given

Я также хотел бы иметь возможность использовать return User::firstByAttribtues($value);, но Mockery не позволяет мне издеваться над защищенным методом - есть ли способ обойти это?


person Dwight    schedule 13.05.2014    source источник


Ответы (2)


Мне пришлось копаться в исходном коде Mockery, чтобы найти это, но смотрели ли вы на shouldAllowMockingProtectedMethods?

То есть, чтобы имитировать класс foo и разрешить имитировать защищенные методы:

$bar = \Mockery::mock('foo')->shouldAllowMockingProtectedMethods();
// now set your expectations up

а затем продолжайте идти оттуда.

person Alex Goodwin    schedule 08.01.2015

Не уверен, почему вы не получаете сообщение об ошибке, например, о неожиданном вызове метода firstOrFail. Но, на первый взгляд, я думаю, что проблема в том, что ваш маршрут модели, определенный в routes.php, также вызывает метод firstOrFail.

Итак, ваш тест должен выглядеть примерно так:

public function testShowPage()
{
    $stubQuery = \Mockery::mock('Illuminate\Database\Eloquent\Builder');
    $this->mock->shouldReceive('whereSlug')->once()->andReturn($stubQuery);
    $stubQuery->shouldReceive('firstOrFail')->andReturn($this->mock);


    $this->action('GET', 'UsersController@show');

    $this->assertResponseOk();
    $this->assertViewHas('user');
}
person awei    schedule 07.06.2014