In this example we are going to rollback changes done to data in our database so that we don't have to reload Doctrine DataFixtures for each tests. This will increase test performace becase we load DataFixtures only once at the beginning of the whole test suite and rollback any changes done after each test cases. As a result our tests won't break even though they all depend on same data.


AbstractTest


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 Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class AbstractTest extends WebTestCase
{
/** @var Client $client */
protected $client;
/** @var EntityManagerInterface */
private $entityManager;

public static function setUpBeforeClass()
{
$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());
}

protected function setUp()
{
$this->client = static::createClient();
$this->client->disableReboot();

$this->entityManager = $this->client->getContainer()->get('doctrine.orm.entity_manager');
$this->entityManager->beginTransaction();
$this->entityManager->getConnection()->setAutoCommit(false);
}

protected function tearDown()
{
if ($this->entityManager->getConnection()->isTransactionActive()) {
$this->entityManager->rollback();
}
}

private static function getFixtures(): iterable
{
return [
new CountryFixtures(),
];
}
}

CountryControllerTest


The update_one method below actually changes the code and name values of Great Britain record in database. I've just used GET method to keep this example as short as possible for demonstration purposes. The code is changed as abc and name is changed as Hello in the background. As you will see in the test result below, the get_all_returns_valid_response_2 didn't break!


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_1(int $code, string $body): void
{
$this->client->request('GET', '/api/v1/countries');

$this->assertSame($code, $this->client->getResponse()->getStatusCode());
$this->assertSame($code, $this->client->getResponse()->getStatusCode());
$this->assertSame($body, $this->client->getResponse()->getContent());
}

/**
* @test
*/
public function update_one(): void
{
$this->client->request('GET', '/api/v1/countries/1');

$this->assertSame(200, $this->client->getResponse()->getStatusCode());
}

/**
* @test
* @dataProvider getAllDataProvider
*/
public function get_all_returns_valid_response_2(int $code, string $body): void
{
$this->client->request('GET', '/api/v1/countries');

$this->assertSame($code, $this->client->getResponse()->getStatusCode());
$this->assertSame($body, $this->client->getResponse()->getContent());
}

public function getAllDataProvider(): iterable
{
return [
[
'$code' => 200,
'$body' => <<[
{
"id": 1,
"code": "gb",
"name": "Great Britain",
},
{
"id": 2,
"code": "tr",
"name": "Turkey",
},
{
"id": 3,
"code": "de",
"name": "Germany",
}
]
EOT
]
];
}
}

Test


$ vendor/bin/phpunit --filter CountryControllerTest tests/Controller/CountryControllerTest.php 
PHPUnit 7.1.5 by Sebastian Bergmann and contributors.

... 3 / 3 (100%)

Time: 592 ms, Memory: 12.00MB

OK (3 tests, 5 assertions)