Paulund

Laravel Smoke Test Guest URLs

In this tutorial we're going to build a test class that will run through all guest URLs defined in our web.php routes and check that they all return a HTTP status of 200. This is allow us to quickly check all open URLs and make sure that there aren't any errors on these page.

Defining Your Routes

When you define your routes in Laravel you'll add them to the routes/web.php. Using the guest middleware will make sure that anybody can access these URLs without having to be logged in. When the URLs are wrapped in the guest middleware we can make sure we only check these URLs.

<?php
// Guest middleware
Route::group(['middleware' => ['guest']], function(){
    // Home page
    Route::get('/', 'HomeController@index');

    // Tutorials
    Route::get('/tutorials', 'TutorialController@index');
    Route::get('/tutorials/{tutorialSlug}', 'TagsController@show');

    // Blog Post
    Route::get('/{postSlug}', 'PostController@show');
});

Create Smoke Test Class

Laravel allows use to make HTTP request tests to where we can make a GET request to all the URLs in our web.php file and assert the status of the response. To build this test class we'll need to do the following.

  • Get all guest routes
  • Loop through each route and check if the URL is dynamic
  • If the URL is dynamic then create a test URL
  • Make a HTTP request to this URL
  • Check if URL returns 200 HTTP status

Get All Guest Routes

To get all routes that are defined in Laravel you can use Illuminate\Routing\Router with the method getRoutes method. This will return all routes defined in your application. We need to then loop through all of these, check the middleware is set to guest, remove any dusk added routes and return these routes.

<?php
private function getGuestRoutes()
{
    return array_filter(app(Router::class)->getRoutes()->getRoutes(), function ($route) {
        $middleware = $route->middleware();
        return in_array('guest', $middleware) &&
                strpos($route->uri(), '_dusk') === false ? $route : false;
    });
}

Convert Dynamic Routes

After we have all the routes we need to then loop through the routes, check if they're dynamic and check a test model to match the dynamic route. First we need to map our dynamic routes to a model to create the route.

<?php

protected $dynamicMappings = [
    'tutorialSlug' => Tutorial::class,
    'postSlug' => Post::class
];

Then we can pass in the route into this function, check to see if we need to change the dynamic route, create a new model and return the new route for the model.

<?php
/**
 * @param $url
 *
 * @return mixed
 */
private function convertMapping($url)
{
    foreach($this->dynamicMappings as $mapping => $modelClass)
    {
        $stringMapping = '{'.$mapping.'}';
        if(strpos($url, $stringMapping) !== false) {
            factory($modelClass)->create();
            return str_replace($stringMapping, app($modelClass)->first()->slug, $url);
        }
    }

    return $url;
}

The return of dynamic URL mapping function can be used to make a HTTP request then we can check to see if the HTTP status is a 200.

Full Test Class

Below is the full test class to use in your application.

<?php

namespace Tests\Feature\SmokeTests;

use App\Models\Post;
use App\Models\Tutorial;
use Illuminate\Routing\Router;
use Tests\TestCase;

class SmokeGuestTest extends TestCase
{
    protected $dynamicMappings = [
        'tutorialSlug' => Tag::class,
        'postSlug' => Post::class
    ];

    public function testCheckGuestRoutes()
    {
        foreach($this->getGuestRoutes() ?? [] as $route) {
            $url = $this->convertMapping( $route->uri );
            $response = $this->get($url);
            $response->assertStatus(200);
        }
    }

    /**
     * Get all the urls that can be accessed by a guest user
     */
    private function getGuestRoutes()
    {
        return array_filter(app(Router::class)->getRoutes()->getRoutes(), function ($route) {
            $middleware = $route->middleware();
            return in_array('guest', $middleware) &&
                    strpos($route->uri(), '_dusk') === false ? $route : false;
        });
    }

    /**
     * @param $url
     *
     * @return mixed
     */
    private function convertMapping($url)
    {
        foreach($this->dynamicMappings as $mapping => $modelClass)
        {
            $stringMapping = '{'.$mapping.'}';
            if(strpos($url, $stringMapping) !== false) {
                factory($modelClass)->create();
                return str_replace($stringMapping, app($modelClass)->first()->slug, $url);
            }
        }

        return $url;
    }
}