Skip to content

Laravel: Testing get requests with query string

Laravel provides us with a range of test helper methods as part of the framework, and some of the crucial ones that we are using on a daily basis are sitting a trait name MakesHttpRequests. These are methods we are using to send a request to our application and eventually make an assertion against the response or the actions that happened.

But you may notice the get method on MakesHttpRequests does not support query string (parameters):

/**
 * Visit the given URI with a GET request.
 *
 * @param  string  $uri
 * @param  array  $headers
 * @return \Illuminate\Testing\TestResponse
 */
public function get($uri, array $headers = [])
{
    $server = $this->transformHeadersToServerVars($headers);
    $cookies = $this->prepareCookiesForRequest();

    return $this->call('GET', $uri, [], $cookies, [], $server);
}

As you see the third parameter for call method is an empty array.

This could be a problem for you if you are trying to test a route that accepts query strings. Let me show you 3 options to overcome this issue:

1- Use CALL method instead of GET

If you look at the underlying code of the get method you notice it delegates the action to the call method at the end. So what we can do is to use that method in our test.

/** @test */
public function it_searches_users_using_query_string()
{
    $this->call('GET', '/users', ['search' => 'houmaan'])
        ->assertSuccessful()
        ->assertSee('houmaan');
}

2- Override GET method

Although, option 1 might work but we ignored two methods are called in base get method:

$server = $this->transformHeadersToServerVars($headers);
$cookies = $this->prepareCookiesForRequest();

As the method names clearly convey what they do we want to make sure we call them as part of get request, so what you can do is to override the get method in our TestCase and add an additional argument for parameters:

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    public function get($uri, array $parameters = [], array $headers = [])
    {
        $server = $this->transformHeadersToServerVars($headers);
        $cookies = $this->prepareCookiesForRequest();

        return $this->call('GET', $uri, $parameters, $cookies, [], $server);
    }
}

Now we can call get method in our test like this:

/** @test */
public function it_searches_users_using_query_string()
{
    $this->get('/users', ['search' => 'houmaan'])
        ->assertSuccessful()
        ->assertSee('houmaan');
}

3- Use route helper method

The last option, and it’s the option I personally take, is to name our route and use the helper to attach query string to it. So by using name method on the route file we can assign a name to our route:

Route::get('/users', 'UsersController@index')->name('users.index');

And then simply you can use route helper function on our test and pass an array of parameters as the second argument.

/** @test */
public function it_searches_users_using_query_string()
{
    $this->get(route('users.index', ['search' => 'houmaan']))
        ->assertSuccessful()
        ->assertSee('houmaan');
}

So what routehelper method did for us here is to lookup the key users.index in the routes, find the url, transform the array of parameters (second argument) to query string and attach to it.

Conclusion

Although all 3 options could be valid, I lean more toward option 3! Because not only it solves my problem with the get method, having a name for your routes will free you from remembering all cryptic routes in your application, plus by the time you want to refactor or rename a route, tests could remain unchanged.

Published inTutorial

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

© 2020 - houmaan.dev