Laravel Tests
Requires Production Plan

Laravel provides a framework for functional tests, working with PHPUnit.

If you already implemented such tests, you may want to re-use them for your performance tests using Blackfire Builds, and avoid to write your test scenarios twice.

Using the Blackfire SDK, you can leverage these functional tests in order to create Blackfire Builds, directly from your CI system.

It relies on Symfony HTTP Client in order to send real HTTP requests in the `testing` environment for the profiled URLs.

  • PHP >= 7.3
  • Laravel >= 8.x
  • PHPUnit >= 9.3

Add Blackfire PHP SDK as a dependency in your project (1.29+ version), together with Symfony HTTP Client.

1
Loading...

Edit your phpunit.xml.dist:

1
2
3
4
5
6
7
8
9
10
<!-- phpunit.xml.dist -->
    <extensions>
        <!-- Blackfire extension -->
        <extension class="\Blackfire\Bridge\PhpUnit\Laravel\BlackfireBuildExtension">
            <arguments>
                <string><!-- Your environment name or UUID --></string>
                <string><!-- Name for the build (optional) --></string>
            </arguments>
        </extension>
    </extensions>

Create a env.testing file at the root of your Laravel application:

1
APP_ENV=testing

Edit your app/Http/Kernel.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

...

use Blackfire\Bridge\Laravel\BlackfireTestHttpRequestsTrait;

class Kernel extends HttpKernel
{
    use BlackfireTestHttpRequestsTrait;

    protected $middleware = [
        // ...

        \Blackfire\Bridge\Laravel\InstrumentedTestRequests::class,
    ];

    // ...

Edit your app/Console/Kernel.php:

1
2
3
4
5
6
7
8
9
10
11
<?php

// ...

use Blackfire\Bridge\Laravel\BlackfireTestArtisanCommandsTrait;

class Kernel extends ConsoleKernel
{
    use BlackfireTestArtisanCommandsTrait;

    // ...

In order to use your functional tests with Blackfire, you need to extend the BlackfireTestCase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace Tests\Feature;

use Blackfire\Bridge\Laravel\BlackfireTestCase;

class FooTest extends BlackfireTestCase
{
    // Give a title to the scenario.
    protected $blackfireScenarioTitle = 'Name of this scenario';

    public function test_get(): void
    {
        $response = $this->get('/foo');

        $response->assertStatus(200);
    }
}

A scenario is created for each instance of BlackfireTestCase, each request being sent constituting a step of the scenario.

You can specify the title for the ongoing scenario by setting the $blackfireScenarioTitle protected variable within your test case, like in the example above.

By default, every requests executed/tested within the test case` are profiled.

You may want to temporarily disable the profiling process for a few requests. This is possible using the disableProfiling() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace Tests\Feature;

use Blackfire\Bridge\Laravel\BlackfireTestCase;

class FooTest extends BlackfireTestCase
{
    // Give a title to the scenario.
    protected $blackfireScenarioTitle = 'Name of this scenario';

    public function test_get(): void
    {
        $response = $this
            ->disableProfiling()
            ->get('/foo');

        $response->assertStatus(200);
    }
}

You may want to enable the profiling process only for selected requests. This is possible by disabling the automatic profiling of all requests and by using the enableProfiling() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace Tests\Feature;

use Blackfire\Bridge\Laravel\BlackfireTestCase;

class FooTest extends BlackfireTestCase
{
    // Disable the automatic profiling of all requests
    protected $profileAllRequests = false;

    // Give a title to the scenario.
    protected $blackfireScenarioTitle = 'Name of this scenario';

    public function test_get(): void
    {
        $response = $this
            ->enableProfiling()
            ->get('/foo');

        $response->assertStatus(200);
    }
}

The title of a profile triggered from the tests will be based on the URL and the request's method. This title can be manually defined using the setProfileTitle method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace Tests\Feature;

use Blackfire\Bridge\Laravel\BlackfireTestCase;

class FooTest extends BlackfireTestCase
{
    // Give a title to the scenario.
    protected $blackfireScenarioTitle = 'Name of this scenario';

    public function test_get(): void
    {
        $response = $this
            ->setProfileTitle('This Profile has a name')
            ->get('/foo');

        $response->assertStatus(200);
    }
}

Artisan Commands can be profiled as well using the BlackfireTestCase.

The profiles of CLI commands cannot be included within the build. The profile URL will be printed if the shell output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace Tests\Feature;

use Blackfire\Bridge\Laravel\BlackfireTestCase;

class FooTest extends BlackfireTestCase
{
    // Give a title to the scenario.
    protected $blackfireScenarioTitle = 'Name of this scenario';

    public function test_command(): void
    {
        $this
            ->artisan('list')
            ->assertExitCode(0);
        ;
    }
}

In order to compare the current build to another one, you may set BLACKFIRE_EXTERNAL_ID and BLACKFIRE_EXTERNAL_PARENT_ID environment variables when launching your tests:

1
2
3
BLACKFIRE_EXTERNAL_ID=current_build_reference \
BLACKFIRE_EXTERNAL_PARENT_ID=parent_build_reference \
php artisan test

You may use Git commit identifiers as references.

You may want to run Blackfire tests in a separate job in your pipeline, while still running your functional tests.

In this case, it is possible to globally disable the Blackfire build by setting the BLACKFIRE_BUILD_DISABLED environment variable to 1:

1
BLACKFIRE_BUILD_DISABLED=1 php artisan test

Doing so also disables profiling for every HTTP requests.