11/08/2018 - DOCTRINE, PHPUNIT, SYMFONY
Example below helps us when we deal with dynamic response content while testing Symfony controllers with PHPUnit. In this example the id
and created_at
fields are dynamic so each response would have different values. The reason why the data is always dynamic is because they are loaded by Doctrine DataFixtures.
setUp
method reloads data fixtures before running each test cases.replaceDynamicData
method replaces any dynamic data with a static one for given rules.tearDownAfterClass
method reloads data fixtures when whole test suite ends.These configurations are for isolating test
, dev
and prod
environments so each environment will have their own databases such as api_test
, api_dev
and api
.
APP_ENV=dev
DATABASE_URL=mysql://root:root@127.0.0.1:3306/api
doctrine:
dbal:
...
url: '%env(resolve:DATABASE_URL)%'
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%_%kernel.environment%'
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%_%kernel.environment%'
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php">
tests
src
Make sure you have phpunit/phpunit
and symfony/browser-kit
composer packages installed.
declare(strict_types=1);
namespace App\Tests;
use App\DataFixtures\CountryFixtures;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class AbstractTest extends WebTestCase
{
/** @var Client $client */
protected $client;
public static function tearDownAfterClass()
{
self::reloadDataFixtures();
}
protected function setUp()
{
self::reloadDataFixtures();
$this->client = static::createClient();
}
protected function replaceDynamicData(string $data): string
{
return preg_replace(
[
'/"id":\d/',
'/\b(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\+(\d{4})\b/',
],
[
'"id":AUTO_ID',
'DATE_ISO8601',
],
$data
);
}
private static function reloadDataFixtures(): void
{
$kernel = static::createKernel();
$kernel->boot();
$entityManager = $kernel->getContainer()->get('doctrine')->getManager();
$loader = new Loader();
foreach (self::getFixtures() as $fixture) {
$loader->addFixture($fixture);
}
$purger = new ORMPurger();
$purger->setPurgeMode(ORMPurger::PURGE_MODE_DELETE);
$executor = new ORMExecutor($entityManager, $purger);
$executor->execute($loader->getFixtures());
}
private static function getFixtures(): iterable
{
return [
new CountryFixtures(),
];
}
}
declare(strict_types=1);
namespace App\Tests\Controller;
use App\Tests\AbstractTest;
class CountryControllerTest extends AbstractTest
{
/**
* @test
* @dataProvider getAllDataProvider
*/
public function get_all_returns_valid_response(int $code, string $body)
{
$this->client->request('GET', '/api/v1/countries');
$this->assertSame($code, $this->client->getResponse()->getStatusCode());
$this->assertSame(
$this->replaceDynamicData(json_encode(json_decode($body, true))),
$this->replaceDynamicData($this->client->getResponse()->getContent())
);
}
public function getAllDataProvider(): iterable
{
return [
[
'$code' => 200,
'$body' => <<[
{
"id": 1,
"code": "gb",
"name": "Great Britain",
"createdAt": "2000-10-29T21:57:00+0100"
},
{
"id": 2,
"code": "tr",
"name": "Turkey",
"createdAt": "2001-11-30T22:58:01+0200"
},
{
"id": 3,
"code": "de",
"name": "Germany",
"createdAt": "2002-12-31T23:59:02+0300"
}
]
EOT
]
];
}
}
$ vendor/bin/phpunit --filter CountryControllerTest tests/Controller/CountryControllerTest.php
PHPUnit 7.1.5 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 1.77 seconds, Memory: 22.00MB
OK (1 test, 2 assertions)