Nov 8, 2017 4 min read

Laravel Feature Tests Using Regex Patterns

I recently created a Laravel app that implemented swappable repositories to create random numbers.

  • Build
  • Design
Jon Berbaum

Jon Berbaum
President

I recently created a Laravel app that implemented swappable repositories to create random numbers. One of the repositories would be injected into a controller. The app is actually a tutorial/demo to show off Laravel’s Automatic Injection and random number generators seemed like an interesting way to demonstrate how to inject one particular implementation out of several. I decided to create one implementation using the standard random number generator, another using the Mersenne Twister generator, and a third using a cryptographically secure generator.

TDD Circle of Life showing three arrows, moving in a circle with headers that say Test Fails, Test Passes, and Refactor

I often use TDD in my process so the first test I wrote was to test for the generation of a random number. My test would drive my implementation. I created a Feature test using http-test and quickly realized that the available assertions did not contain any regex style assertions. All I wanted to do was assert that the returned content include a random number between 0 and 100. A simple pattern match on a regex of 1 to 3 digits would do nicely. But how to do this?

Laravel’s http-test capabilities are intended to make testing APIs, URLs and fixed content simple. Unfortunately, a regex pattern matching assertion is not included. To solve this shortcoming, I needed to get the actual rendered content and use PHPUnit assertions. This turned out to be quite easy since http-test tests are a superset of PHPUnit and all PHPUnit assertions are already available. This approach can be used to leverage any PHPUnit style assertion or even create a custom assertion when needed.

This example assumes a controller and route that will generate a string that includes a random number.

<?php
// app/Http/Controllers/RandomController.phpnamespace App\Http\Controllers;use Illuminate\Http\Request;class RandomController extends Controller
{
    public function index()
    {
     return sprintf("Hello and the winning number is %d!", rand(0,100));
    }
}

We need a route to invoke the controller.

<?php
// routes/web.phpRoute::get('/random', 'RandomController@index');

And finally the test.

<?php
// tests/Feature/CanMatchRegexTest.phpnamespace Tests\Feature;use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;class CanMatchRegexTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        //$this->assertTrue(true);
        $response = $this->get('/random');        $response->assertStatus(200);
        $response->assertSeeText('Hello');        // expect content: 'Hello and the winning number is <number>!' where number is >= 0 and <= 100
        // get the actual page content
        $content = $response->getContent();
        // assert a regex
        $this->assertRegExp('/^Hello .* winning number is \d+!$/', $content);
    }
}

The rendered page is available in $response->getContent(). This can be matched using PHPUnit’s assertRegExp() assertion. You can obviously use any PHPUnit assertion or even create a more complex assertion when required.

Here’s a similar example with subtle differences. The rendered page shows a number between 0 and 100. I wanted a single assertion that verified the number to be in that range.

<?php
// tests/Feature/CanViewRandomNumberTest.phpnamespace Tests\Feature;use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;class CanViewRandomNumberTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        $response = $this->get('/random');        $response->assertStatus(200);        // laravel http-tests available-assertions don't include
        // numeric tests - let's roll our own
        // first grab the page content
        $content = $response->getContent();        // create a PHPUnit assertion on both >= and <=
        $this->assertThat(
            $content,
            $this->logicalAnd(
                $this->greaterThanOrEqual(0),
                $this->lessThanOrEqual(100)
            )
        );
    }
}

Sometimes a simple assertSeeText() is sufficient, but sometimes you need more flexibility. Even though Laravel is missing http-test regex assertions out-of-the-box, they are really easy to add when needed.

Testing may seem difficult, confusing, and time consuming, but I find that testing speeds up my process in the long run. And I have much more confidence in my work when I have tests to prove that it works as intended. Finally, I get the benefit of a nice regression suite that grows with the code.

An upside down red lightbulb on the left, frowning that says

Happy testing with regex patterns!

Download the code here.