Testing API controllers in Laravel

Pavel Buchnev
2 min readJun 26, 2020

--

Hello, artisans! I’m Pavel.

I have to cover API methods with tests a lot for different applications and I’ve collected a couple of tricks I would like to tell about.

Preparation

  1. I use Single Action Controllers when I develop Laravel applications, i.e. a controller has only one action. Below you can see an example.
Controllers
- Users
- UpdateAction.php
- CreateAction.php
- IndexAction.php
- Profile
- UpdatePasswordAction.php
- ...
- News
- IndexAction.php
- MarkAsReadAction.php
- ...
...

This approach allows me to easily navigate between actions and not to have fat controllers. In addition to this controllers can be tested easier than with many actions, because the tests’ structure can repeat the controllers’ structure.

Controllers
- Users
- UpdateActionTest.php
- CreateActionTest.php
- IndexActionTest.php
- Profile
- UpdatePasswordActionTest.php
- ...
- News
- IndexActionTest.php
- MarkAsReadActionTest.php
- ...
...

2. Every action has a unique route name and this name is a single source for URL generations for a specific route.

Let’s start simplifying writing tests

Because I have single action controllers , so every controller can be represented with only one TestCase and we can create auxiliary test case that could help us simplify testing of our controllers. I called in ActionTestCase. The full code of it you can see below.

As you can see this test case has only one required method getRouteName, that should return the name of route for the specific testable controller. A route object will be found by the given name and a test case will get all necessary information about it for making requests.

Here an example of testing user registration

And an example of testing user authorization

As you can see I don’t have fat tests and code duplication.

Couple of words about TestCase methods

assertRouteContainsMiddleware(…$names)
Checking if a route contains given middleware names

assertRouteHasExactMiddleware(…$names)
Checking if a route contains only given middleware names.

getRouteByName(): Route
Get a route object. If the route is not found, test would be marked as fail.

callRouteAction(array $data = [], array $parameters = [], array $headers = []): TestResponse
Make an unauthorized request.
$data — Request body
$paramenters — Route parameters
$headers — Request headers

callAuthorizedRouteAction(array $data = [], array $parameters = [], array $headers = [], array $scopes = []): TestResponse
Make an authorized request from random user.

callAuthorizedByUserRouteAction(User $user, array $data = [], array $parameters = [], array $headers = [], array $scopes = []): TestResponse
Make an authorized request from given user.

That’s all folks!

I hope my story would be useful and may encourage you to write something great. Share your tricks in comments, it will be interesting to read.

P.s. Sorry for my English. I’m trying to write good stories, but I need some time to improve my English. If you notice mistakes, let me know.

--

--

Pavel Buchnev
Pavel Buchnev

Written by Pavel Buchnev

Senior PHP Developer | Contributor to Spiral Framework 🚀 | Enthusiast of RoadRunner & long-running applications | Creator of Buggregator

Responses (1)