Reputation:
Good evening :-)
I am learning Symfony(3) now, and I would like to use test for my classes. I've read that unittests shouldn't use databases, mocking its objects rather.
But despite this, I would like to create in setUp()/KernelTestCase database (e.q. MySQL) and read its content for file, next doing tests (simple -unittests), and purge at tearDown().
Is it possible do it with dumped MySQL file?
What is best (? lazy) way to do it?
I would rather to read prepared (dumped) SQL-like file then 'update:schema' from ORM-class. Where to put file in Symfony3? How read it and create database content?
Pls. help me.
Upvotes: 2
Views: 1355
Reputation:
My solution is:
This load all SQL files to MySQL TEST DB - defined in 'parameters_test.yml'
and processing test and drops all DB tables before next test and again with next tests... It can be probably be done shorter and in more right way with command php bin/console doctrine:database:import ...
as @Tokeeen.com said. Thank you for help.
// tests/AppBundle/Repository/BotsDbTest.php
<?php
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Yaml\Parser;
class BotsDbTest extends KernelTestCase
{
private $doctr;
private $db_cred;
private $db;
/**
* {@inheritDoc}
*/
protected function setUp()
{
$kernel = self::bootKernel();
$this->doctr = $kernel->getContainer()
->get('doctrine')
->getManager();
// for tests with loaded content
$this->db = new \AppBundle\Wiks\BotsDb();
// https://symfony.com/doc/current/bundles/extension.html
// get DB credientals from "parameters_test.yml":
$configDirectories = array( 'app/config' );
$locator = new FileLocator( $configDirectories );
$yamlUserFiles = $locator->locate( 'parameters_test.yml', null, false );
// https://davidegan.me/parse-yaml-in-php-using-symfony-yaml/
$yaml = new Parser();
$yaml_array = $yaml->parse( file_get_contents( $yamlUserFiles['0'] ) );
// needen DB is the second in Symfony - as database2 in file "parameters_test.yml":
$prefix_db = 'database2';
// looking for all keys with suffix: eq: 'database2_host'
$needed_sufix = [ 'host', 'port', 'name', 'user', 'password' ];
$this->db_cred = array();
foreach ( $yaml_array[ 'parameters' ] as $key => $value ) {
if ( strpos( $key, $prefix_db ) !== false ) {
foreach ( $needed_sufix as $needed_key ) {
if ( strpos( $key, $needed_key ) !== false ) {
$this->db_cred[ $needed_key ] = $value;
}
}
}
}
if ( count( $this->db_cred ) == count( $needed_sufix ) ) {
// check is all found
/*Array (
[host] => 127.0.0.1
[port] =>
[name] => db_name
[user] => user_name
[password] => ***
) */
$finder = new Finder();
// find and put into mysql all files as prepared content to tests
$finder->files()->name('*.sql');
foreach ( $finder->in( array( 'tests/dbcontent' ) ) as $file ) {
$shell_command = 'mysql --user='.$this->db_cred['user'].' --password='.$this->db_cred['password'];
$shell_command .= ' '.$this->db_cred['name'].'< '.$file->getRealPath();
shell_exec( $shell_command );
}
}
}
/**
* {@inheritDoc}
*/
protected function tearDown()
{
parent::tearDown();
// remoove DB content ( all tabels ):
$shell_command = 'mysqldump --user='.$this->db_cred['user'].' --password='.$this->db_cred['password'].' ';
$shell_command .= '--add-drop-table --no-data '.$this->db_cred['name'].' | ';
$shell_command .= 'grep -e \'^DROP \| FOREIGN_KEY_CHECKS\' | ';
$shell_command .= 'mysql --user='.$this->db_cred['user'].' --password='.$this->db_cred['password'].' '.$this->db_cred['name'];
shell_exec( $shell_command );
$this->doctr->close();
$this->doctr = null; // avoid memory leaks
}
/** tests, tests, tests...
*
*/
public function test_getBots()
{
$res = $this->db->getBots( $this->doctr );
$this->assertEquals(5, count( $res ));
[...]
Helpful links:
How to remove all MySQL tables from the command-line without DROP database permissions?
https://davidegan.me/parse-yaml-in-php-using-symfony-yaml/
https://symfony.com/doc/current/components/finder.html
https://symfony.com/doc/current/bundles/extension.html
Upvotes: 0
Reputation: 738
I am using this for years:
Repeat! This is for me the most efficient as you only load your test data once; NOT for every tests. Of course you will have to think harder of your tests scenario.
For example, think about a crud test:
Test1:
Test2:
Test3:
So the tests must be executed in this order exactly. If you are loading fixtures between each test you don't have to take care of that order but it will make your tests slow.
I feel loading fixtures once is better because it will act as a user would normally do: create, delete, update items... And therefore you can check that there is no side effect between each action.
My load fixtures script:
#!/bin/bash
echo "##########################################################################"
echo "# Refresh data model, reload all reference data, load fixtures, #"
echo "# validate schema for the dev env. #"
echo "##########################################################################"
php bin/console doctrine:database:create --if-not-exists --env=dev
php bin/console doctrine:schema:drop --force --env=dev
php bin/console doctrine:schema:create --env=dev
php bin/console doctrine:schema:validate --env=dev
php bin/console doctrine:fixtures:load -n --env=dev
echo -e " --> DONE\n"
Or if you want to load a database for SQL files, use:
php bin/console doctrine:database:import db.sqb --env=dev
Instead of the load fixtures command.
Then, to launch the tests:
./bin/simple-phpunit --debug --verbose $1
$1
is an argument to specify the test suite to load. (main, front, API, backend...) which you can parameter in your phpunit.xml.dist
file. (you can omit it to run all the tests)
Upvotes: 2