1

I was wondering if anyone else came across this problem. I'm going through Jeffrey Way's book on testing in Laravel and I'm on the chapter which explains how to test controllers.

When I follow the examples from the book - I get the message:

Failed asserting that Illuminate\Http\Response Object (...) is an instance of class "Illuminate\Http\RedirectResponse".

My test is as follow:

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']);

    }

And the very simple method in the controller (just to test this specific scenario):

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!');

    }

From what I can see the AssertionsTrait::assertRedirectedTo checks for instance of 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);
    }

which should work just fine as the Redirect facade resolves to the Illuminate\Routing\Redirector and its route() method calls createRedirect(), which returns the instance of the Illuminate\Http\RedirectResponse - so not quite sure what's causing it.

UPDATE:

Just checked the code again and it looks like the problem is within AssertionsTrait::assertRedirectedTo() method. The call to $this->client->getResponse() returns instance of Illuminate\Http\Response instead of Illuminate\Http\RedirectResponse - hence the $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $response) call fails. But I'm still not sure why - I'm extending the TestCase which is suppose to take care of all environment setup etc. Any idea?

Sebastian Sulinski
  • 5,815
  • 7
  • 39
  • 61
  • 2
    Assuming that you're working with a resource, the logic you have in the create method should actually be placed in the store method. posts/create should just return the view with the form to input the data, the store method then takes the input data and actually creates and stores the object. So: `$this->post('posts', $input);` does not call your `create()` method, but your `store()` method. So take look at it, if it's not there you're probably just getting a 404 Response, which is not a redirect. – Quasdunk Jan 18 '15 at 12:05
  • You're right @Quasdunk - thanks for pointing this out. Can you post it as the answer so that I can accept it? – Sebastian Sulinski Jan 18 '15 at 15:23

1 Answers1

1

Posting comment as answer:

Since you're working with a resourceful object, Laravel automatically creates some routes for you, namely:

+-----------------------------+---------------+-------------------------+
| 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 |
+-----------------------------+---------------+-------------------------+

As you can see, a POST request to /posts (as you do it in your test) triggers the store() method on your PostsController, not the create() method which you assumed to be under test.

create and store as well as edit and update can be confusing sometimes. Here's the difference:

  • create() - show the form for creating a new resource
  • store() - actually create the resource from the posted data

  • edit($id) - show the form for editing the specified resource
  • update($id) - actually update the resource with the posted data
Quasdunk
  • 14,944
  • 3
  • 36
  • 45