Symfony provides a useful 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 uses Symfony Panther in the background, in order to send real HTTP requests, and to manage the PHP built-in webserver.
Despite the use of Symfony Panther, this integration does not use ChromeDriver nor GeckoDriver, and thus does not support JavaScript.
Add Blackfire PHP SDK as a dependency in your project, together with Symfony Panther:
1
Edit your phpunit.xml.dist
:
1 2 3 4 5 6 7 8 9 10 11 12
<!-- phpunit.xml.dist -->
<extensions>
<!-- Add Symfony Panther extension -->
<extension class="Symfony\Component\Panther\ServerExtension" />
<!-- Blackfire extension -->
<extension class="\Blackfire\Bridge\PhpUnit\BlackfireBuildExtension">
<arguments>
<string><!-- Your environment name or UUID --></string>
<string><!-- Name for the build (optional) --></string>
</arguments>
</extension>
</extensions>
In order to use your functional tests with Blackfire, you need to either extend
the BlackfireTestCase
or import the BlackfireTestCaseTrait
.
Then, you can use the BlackfiredHttpBrowserClient
instead of the Symfony
functional test client.
Extending BlackfireTestCase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
namespace App\Tests\Controller;
use App\Pagination\Paginator;
use Blackfire\Bridge\PhpUnit\BlackfireTestCase;
class BlogControllerTest extends BlackfireTestCase
{
// Give a title to the scenario.
protected const BLACKFIRE_SCENARIO_TITLE = 'Blog Controller';
public function testIndex(): void
{
// Create the Blackfire enabled HTTP browser.
$client = static::createBlackfiredHttpBrowserClient();
$crawler = $client->request('GET', '/en/blog/');
$this->assertResponseIsSuccessful();
$this->assertCount(
Paginator::PAGE_SIZE,
$crawler->filter('article.post'),
'The homepage displays the right number of posts.'
);
}
}
Using BlackfireTestCaseTrait:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
namespace App\Tests\Controller;
use App\Pagination\Paginator;
use Blackfire\Bridge\PhpUnit\BlackfireTestCaseTrait;
use PHPUnit\Framework\TestCase;
class BlogControllerTest extends TestCase
{
use BlackfireTestCaseTrait;
// Give a title to the scenario.
protected const BLACKFIRE_SCENARIO_TITLE = 'Blog Controller';
public function testIndex(): void
{
// Create the Blackfire enabled HTTP browser.
$client = static::createBlackfiredHttpBrowserClient();
$crawler = $client->request('GET', '/en/blog/');
$this->assertResponseIsSuccessful();
$this->assertCount(
Paginator::PAGE_SIZE,
$crawler->filter('article.post'),
'The homepage displays the right number of posts.'
);
}
}
A scenario is created for each instance of BlackfireTestCase
/
BlackfireTestCaseTrait
, each request being sent constituting a step of
the scenario.
You can specify the title for the ongoing scenario by setting the
BLACKFIRE_SCENARIO_TITLE
class constant within your test case, like
in the example above.
Behind the scenes, Panther automatically starts the PHP built-in server. Please refer to Panther documentation if you want to customize the webserver.
A nice alternative to the PHP built-in server is the Symfony Local Web Server.
To do this, you need to set the PANTHER_EXTERNAL_BASE_URI
environment variable with the complete base URI of Symfony server endpoint:
1 2 3 4
<!-- phpunit.xml.dist -->
<php>
<server name="PANTHER_EXTERNAL_BASE_URI" value="https://localhost:8000" force="true" />
</php>
You also need to ensure that it is running before launching the PHPUnit test suite.
By default, a scenario is created for each
instance of BlackfireTestCase
and BlackfireTestCaseTrait
, each request
being sent constituting a step of the scenario.
You may want to control this behavior and create the scenarios manually.
To do this, you need to define a BLACKFIRE_SCENARIO_AUTO_START
class constant
in your test case, and set it to false
.
You can then use the BuildHelper
to create and end your scenarios:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
namespace App\Tests\Controller;
use App\Pagination\Paginator;
use Blackfire\Bridge\PhpUnit\BlackfireTestCase;
class BlogControllerTest extends BlackfireTestCase
{
// Disable Blackfire Scenario Auto-Start.
protected const BLACKFIRE_SCENARIO_AUTO_START = false;
public function testIndex(): void
{
// Create the scenario.
$buildHelper = BuildHelper::getInstance();
$buildHelper->createScenario('Title for my scenario');
// Create the Blackfire enabled HTTP browser.
$client = static::createBlackfiredHttpBrowserClient();
$crawler = $client->request('GET', '/en/blog/');
$this->assertResponseIsSuccessful();
$this->assertCount(
Paginator::PAGE_SIZE,
$crawler->filter('article.post'),
'The homepage displays the right number of posts.'
);
// Don't forget to end the scenario.
$buildHelper->endCurrentScenario();
}
}
By default, every requests sent by the BlackfiredHttpBrowserClient
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 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
namespace App\Tests\Controller;
use App\Pagination\Paginator;
use Blackfire\Bridge\PhpUnit\BlackfireTestCase;
class BlogControllerTest extends BlackfireTestCase
{
// Give a title to the scenario.
protected const BLACKFIRE_SCENARIO_TITLE = 'Blog Controller';
public function testNewComment(): void
{
// Create the Blackfire enabled HTTP browser.
$client = static::createBlackfiredHttpBrowserClient();
$client->followRedirects();
// Find first blog post
$crawler = $client->request('GET', '/en/blog/');
$postLink = $crawler->filter('article.post > h2 a')->link();
$client->click($postLink);
$client->clickLink('Sign in');
// Temporarily disable profiling.
$client->disableProfiling();
$client->submitForm('Sign in', [
'_username' => 'john_user',
'_password' => 'kitten',
]);
// Re-enable profiling.
$client->enableProfiling();
$crawler = $client->submitForm('Publish comment', [
'comment[content]' => 'Hi, Symfony!',
]);
$newComment = $crawler->filter('.post-comment')->first()->filter('div > p')->text();
$this->assertSame('Hi, Symfony!', $newComment);
}
}
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 \
bin/phpunit tests/Controller/BlogControllerTest
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 bin/phpunit tests/Controller/BlogControllerTest
Doing so also disables profiling for every HTTP requests sent by the
BlackfiredHttpBrowser
.
setUpBeforeClass()
and tearDownAfterClass()
¶
The BlackfireTestCaseTrait
leverages the setUpBeforeClass()
and
tearDownAfterClass()
methods from the base PHPUnit TestCase
class.
If you already use them within a test case that you want to use with Blackfire, the scenario auto-start will not work, as the mentioned methods will be overridden by the ones defined in your test case class.
In this case, you need to create the scenarios manually.