2

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.

2 Answers2

2

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:

  • Check that list has 1 item (loaded by fixtures)

Test2:

  • Create an item
  • Check that list has 2 items

Test3:

  • Delete an element
  • Check that the list has 1 item

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)

Tokeeen.com
  • 718
  • 7
  • 19
  • Thank you very much for helpfull answer. I have to re-think my ideas about tests. At the beginning I intend to simply load MySQL database from previously prepared dumped file .sql .Problem was, that I don't know, how do it with picked from Symfony DB credientals... My class executes analyse of a few tables content, and this way (download prepared .sql for test) seems to be optimal. –  Nov 25 '17 at 10:12
  • 1
    What can also use the `doctrine:database:import` command in order to import a database from SQL files. – Tokeeen.com Nov 25 '17 at 14:33
  • pls point short solution with `doctrine:database:import` ...I couln't find anything ...understandable for me ...yet :-( –  Nov 25 '17 at 16:22
  • 1
    Run: `php bin/console doctrine:database:import db.sql --env=dev` (replace db.sql) by the sql script of your database. – Tokeeen.com Nov 25 '17 at 16:27
  • Thank you a lot!!! this work!!! `wiks@asus:/var/www/sfproject_v1$ php bin/console doctrine:database:import tests/dbcontent/wiks_comunication_5tbl.sql --env=dev Processing file '/var/www/sfproject_v1/tests/dbcontent/wiks_comunication_5tbl.sql'... OK!` –  Nov 25 '17 at 16:27
0

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