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.

1
  • Super, downwote is simpler then help Commented Nov 24, 2017 at 23:00

2 Answers 2

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)

Sign up to request clarification or add additional context in comments.

5 Comments

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.
What can also use the doctrine:database:import command in order to import a database from SQL files.
pls point short solution with doctrine:database:import ...I couln't find anything ...understandable for me ...yet :-(
Run: php bin/console doctrine:database:import db.sql --env=dev (replace db.sql) by the sql script of your database.
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!
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.