Laravel 4.2: тестирование с помощью assertRedirectedToRoute не выполняется

Мне было интересно, сталкивался ли кто-нибудь еще с этой проблемой. Я просматриваю книгу Джеффри Уэя по тестированию в Laravel и читаю главу, в которой объясняется, как тестировать контроллеры.

Когда я следую примерам из книги - я получаю сообщение:

Не удалось подтвердить, что объект Illuminate\Http\Response (...) является экземпляром класса «Illuminate\Http\RedirectResponse».

Мой тест выглядит следующим образом:

public function testStoreFails()
    {

        $input = ['title' => ''];

        $this->mock
                ->shouldReceive('create')
                ->once()
                ->with($input);

        $this->app->instance('Post', $this->mock);

        $this->post('posts', $input);

        $this->assertRedirectedToRoute('posts.create');

        $this->assertSessionHasErrors(['title']);

    }

И очень простой метод в контроллере (только для проверки этого конкретного сценария):

public function create()
    {

        $input = Input::all();

        $validator = Validator::make($input, ['title' => 'required']);

        if ($validator->fails()) {

            return Redirect::route('posts.create')
                    ->withInput()
                    ->withErrors($validator->messages());

        }

        $this->post->create($input);

        return Redirect::route('posts.index')
                ->with('flash', 'Your post has been created!');

    }

Из того, что я вижу, AssertionsTrait::assertRedirectedTo проверяет, например, Illuminate\Http\RedirectResponse

/**
     * Assert whether the client was redirected to a given URI.
     *
     * @param  string  $uri
     * @param  array   $with
     * @return void
     */
    public function assertRedirectedTo($uri, $with = array())
    {
        $response = $this->client->getResponse();

        $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $response);

        $this->assertEquals($this->app['url']->to($uri), $response->headers->get('Location'));

        $this->assertSessionHasAll($with);
    }

    /**
     * Assert whether the client was redirected to a given route.
     *
     * @param  string  $name
     * @param  array   $parameters
     * @param  array   $with
     * @return void
     */
    public function assertRedirectedToRoute($name, $parameters = array(), $with = array())
    {
        $this->assertRedirectedTo($this->app['url']->route($name, $parameters), $with);
    }

который должен работать нормально, поскольку фасад Redirect разрешается в Illuminate\Routing\Redirector, а его метод route() вызывает createRedirect(), который возвращает экземпляр Illuminate\Http\RedirectResponse, поэтому не совсем уверен, что его вызывает.

ОБНОВЛЕНИЕ:

Только что еще раз проверил код, и похоже, что проблема в методе AssertionsTrait::assertRedirectedTo(). Вызов $this->client->getResponse() возвращает экземпляр Illuminate\Http\Response вместо Illuminate\Http\RedirectResponse, следовательно, вызов $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $response) завершается ошибкой. Но я до сих пор не уверен, почему - я расширяю TestCase, который должен позаботиться обо всех настройках среды и т. Д. Есть идеи?


person Sebastian Sulinski    schedule 18.01.2015    source источник
comment
Предполагая, что вы работаете с ресурсом, логика, которую вы имеете в методе создания, должна быть фактически помещена в метод хранилища. posts/create должен просто возвращать представление с формой для ввода данных, затем метод store берет входные данные и фактически создает и сохраняет объект. Итак: $this->post('posts', $input); вызывает не ваш метод create(), а ваш метод store(). Так что взгляните на него, если его там нет, вы, вероятно, просто получаете ответ 404, который не является перенаправлением.   -  person Quasdunk    schedule 18.01.2015
comment
Вы правы @Quasdunk - спасибо, что указали на это. Можете ли вы опубликовать это как ответ, чтобы я мог принять его?   -  person Sebastian Sulinski    schedule 18.01.2015


Ответы (1)


Публикация комментария как ответа:

Поскольку вы работаете с находчивым объектом, Laravel автоматически создает для вас некоторые маршруты, а именно:

+-----------------------------+---------------+-------------------------+
| URI                         | Name          | Action                  |
+-----------------------------+---------------+-------------------------+
| GET|HEAD posts              | posts.index   | PostsController@index   |
| GET|HEAD posts/create       | posts.create  | PostsController@create  |
| POST posts                  | posts.store   | PostsController@store   |
| GET|HEAD posts/{posts}      | posts.show    | PostsController@show    |
| GET|HEAD posts/{posts}/edit | posts.edit    | PostsController@edit    |
| PUT posts/{posts}           | posts.update  | PostsController@update  |
| PATCH posts/{posts}         |               | PostsController@update  |
| DELETE posts/{posts}        | posts.destroy | PostsController@destroy |
+-----------------------------+---------------+-------------------------+

Как видите, POST-запрос к /posts (как вы делаете это в своем тесте) запускает метод store() в вашем PostsController, а не метод create(), который, как вы предполагали, тестируется.

create и store, а также edit и update иногда могут сбивать с толку. Вот разница:

  • create() - показать форму создания нового ресурса
  • store() - фактически создать ресурс из опубликованных данных

  • edit($id) - показать форму редактирования указанного ресурса
  • update($id) - фактически обновить ресурс с размещенными данными
person Quasdunk    schedule 18.01.2015