Last active
September 11, 2020 08:05
-
-
Save wowo/7137331 to your computer and use it in GitHub Desktop.
IsolatedTestsTrait helps with running functional/integration tests in isolation, with same start state for each test. It's also optimized, keeping in mind performance concerns.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\Example\Tests; | |
use Symfony\Component\Console\Input\ArrayInput; | |
use Symfony\Bundle\FrameworkBundle\Console\Application; | |
use Doctrine\DBAL\Driver\PDOSqlite\Driver as PDOSqliteDriver; | |
require_once(__DIR__ . IsolatedTestsTrait::$kernelRootDir . '/AppKernel.php'); | |
/** | |
* This trait allows you to operate on clean version (with fixtures) of database | |
* It will use sqlite database (needs to be configured in config_test.yml!) | |
* | |
* You can use it to provide isolation in tests for functional tests or tests | |
* where you'd like to use database (some integration tests against fixtures) | |
*/ | |
trait IsolatedTestsTrait | |
{ | |
protected static $application; | |
protected static $environment = 'test'; | |
protected static $debug = false; | |
protected static $fixturesSuffix = '.fixtures'; | |
protected static $fixturesPath = ''; | |
public static $kernelRootDir = '/../../../../app'; | |
/** | |
* Rebuilds (provides clean instance of database) for each test | |
*/ | |
public function setUp() | |
{ | |
parent::setup(); | |
static::rebuildDatabase(); | |
} | |
/** | |
* It will run before any setUps and tests in given test suite | |
* This hook will drop current schema, creat schema and load fixtures | |
* then it will create a copy of the databse, so it will be used in the future tests in this suite | |
*/ | |
public static function setUpBeforeClass() | |
{ | |
static::bootstrapApplication(); | |
} | |
/** | |
* After all tests in given test suite it will remove database copy | |
* Because of this next test suite needs to create its own | |
*/ | |
public static function tearDownAfterClass() | |
{ | |
unlink(static::$fixturesPath); | |
} | |
/** | |
* Runs 3 console commands: (all with -q and -e=test) | |
* doctrine:schema:drop --force | |
* doctrine:schema:create | |
* doctrine:fixtures:load --no-interaction | |
* | |
* After successful database rebuild, it will copy it for further reuse | |
*/ | |
protected function rebuildDatabase() | |
{ | |
$conn = static::$application->getKernel()->getContainer()->get('doctrine.dbal.default_connection'); | |
if (!$conn->getDriver() instanceof PDOSqliteDriver) { | |
throw new \RuntimeException('It would not work nicely with driver other than PDOSqlite'); | |
} | |
$dbPath = $conn->getDatabase(); | |
static::$fixturesPath = $dbPath . static::$fixturesSuffix; | |
if (!file_exists(static::$fixturesPath)) { | |
// create fresh database (schema and fixtures) | |
static::runConsole('doctrine:database:create', array('-n' => true)); | |
static::runConsole('doctrine:schema:drop', array('--force' => true)); | |
static::runConsole('doctrine:schema:create', array()); | |
static::runConsole('doctrine:fixtures:load', array('-n' => true, '--fixtures' => __DIR__ . '/../DataFixtures/')); | |
// copy fresh database to be reused in the future | |
copy($dbPath, static::$fixturesPath); | |
} else { | |
// copy fixtures database to doctrine location | |
copy(static::$fixturesPath, $dbPath); | |
} | |
} | |
/** | |
* Bootstraps console application. It's needed to run commands from the code | |
*/ | |
protected function bootstrapApplication() | |
{ | |
$kernel = new \AppKernel(static::$environment, static::$debug); | |
$kernel->boot(); | |
static::$application = new Application($kernel); | |
static::$application->setAutoExit(false); | |
} | |
/** | |
* It always run with given environment and in quiet mode (no output on the console) | |
*/ | |
protected function runConsole($command, array $options = array()) | |
{ | |
$options['-e'] = self::$environment; | |
$options['-q'] = null; | |
$input = new ArrayInput(array_merge($options, array('command' => $command))); | |
$result = self::$application->run($input); | |
if (0 != $result) { | |
throw new \RuntimeException(sprintf('Something has gone wrong, got return code %d for command %s', $result, $command)); | |
} | |
return $result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment