Practices for database testing in Symfony2? How to isolate?

前端 未结 4 1876
别那么骄傲
别那么骄傲 2021-02-13 19:42

What are the current best practices for testing database interaction with Symfony2? I have a simple CRUD setup and i want to make sure my testing is OK. Right now, i have 4 test

相关标签:
4条回答
  • 2021-02-13 19:52

    testing on local machine is pain in the ... ,so i'm started to using ci system buddy.works (there is free stand-alone version) , and for this i neeeded to resolve this issue on my own.

    result is :

    • all tests works
    • tests are runing on production sql data
    • tests are running in separation (not in dev or production) - so i can do anything that i want with database
    • all pushes to git are tested and i have notification if something is broken
    • all pushes/pull request to deploy branch are automatic uploaded to production

    This is my solution :

    1. second parameters.yml in config with configuration for test
    2. on production i'm making daily sqldump
    3. on starting test on ci this sql backup is copied via scp to test machine
    4. to prepare all this i'm using robo.li ( my config is below)

    /**
    * This is project's console commands configuration for Robo task runner.
    *
    * @see http://robo.li/
    */
    class RoboFile extends \Robo\Tasks
    {
    
    function  loadDb(){
        $this->taskExecStack()
            ->stopOnFail()
            ->exec(" mysql -h mariadb -u root -pqwerty -e 'create database test' ")
            ->exec(" mysql -h mariadb -u root -pqwerty test < test.sql ")
            ->run();
    }
    
    
    function prepareDb(){
        $this->taskExecStack()
            ->stopOnFail()
            ->exec("cp  app/config/parameters-test.yml app/config/parameters.yml")
            ->run();
    
        $this->taskReplaceInFile('app/config/parameters.yml')
            ->from('database_host:     127.0.0.1')
            ->to("database_host:     'mariadb'")
            ->run();
    
        $this->taskReplaceInFile('app/config/parameters.yml')
            ->from('database_user:     dbuser')
            ->to("database_user:     'root'")
            ->run();
    
        $this->taskReplaceInFile('app/config/parameters.yml')
            ->from('database_password: 123')
            ->to("database_password: 'qwerty'")
            ->run();
    
    
    }
    

    }

    i hope it help you to create idea how to organize all this . Using stand alone ci is difficult to setup , but it's really good idea

    0 讨论(0)
  • 2021-02-13 20:09

    The question is pretty old but still valid today so here is my experience and how I handle it today on my Symfony projects.

    I started of using an SQLite in-memory database for my tests and I rebuild the db schema + inserted fixtures before each single test case. This had two major drawbacks:

    • It was still way too slow :(
    • On dev & prod I used Mysql which soon became a problem because SQLite simply does not have all the features needed and sometimes behaves differently

    Using MSQL for the tests and rebuilding the schema + inserting fixtures before each test was simply too slow. So I was looking for alternatives...

    I stumbled across this blog post: http://alexandre-salome.fr/blog/Symfony2-Isolation-Of-Tests

    The guy here suggests to run tests inside active database transactions and simply roll back any changes after every single test.

    I took this idea and created a bundle for it: https://github.com/dmaicher/doctrine-test-bundle

    The setup of the bundle is really easy and does not require changing any existing php test classes. Internally it changes the doctrine config to use custom database connections + driver.

    With this bundle you can simply create your database schema + insert fixtures ONCE BEFORE running the whole testsuite (I prefer doing this in a custom phpunit bootstrap file). Using the phpunit listener all tests will run inside database transactions.

    I've been using this bundle since a while already and for me it works out perfectly using SQLite, MySQL or PostgreSQL.

    Since a while its also used on the symfony-demo project.

    0 讨论(0)
  • 2021-02-13 20:10

    I think it's best to always start clean to be sure that tests are fully isolated. To do that I'm simply building the database structure before each test and than I'm filling it with fixtures needed for a given test.

    Note that I'm only building needed database tables and I'm only inserting needed fixtures. It's a bit faster than loading a big database dump. It's also more clean as tests don't share fixtures (which makes them easier to maintain).

    I have a base test case class called KernelAwareTest which helps me in building the schema. You can find it on gist: https://gist.github.com/1319290

    setUp() boots the Symfony kernel and stores a reference to it in a class property (together with references to the DIC and the entity manager). Also a call to generateSchema() is made to generate the database schema (it uses Schema Tool from Doctrine).

    By default it generates the database structure for all entities known to the entity manager. You can change this behaviour in your test class by overriding the getMetadatas() method.

    P.S.: I tried using in memory database (sqlite) but it wasn't perfect. Anyway it's probably better to run tests against the database you use on production.

    0 讨论(0)
  • 2021-02-13 20:15

    Database testing is always slow as you need to create/drop your schema before/after each test. To avoid unnecessary operations, you could:

    • create schema in the 'setUpBeforeClass' method;
    • ensure your 4 tests are launched in one thread with the '@depend' annotation;
    • drop schema in the 'tearDownAfterClass' method.

    The schema will be created/droped only once for your tests case.

    You can also setup doctrine to use an inmemory sqlite database (which is very fast):

    doctrine:
        dbal:
            driver: pdo_sqlite
            path: :memory:
            memory: true
    

    Anyway, '_construct' and '_destruct' should never be used in phpunit test cases, instead you should use 'setUp' and 'tearDown'.

    0 讨论(0)
提交回复
热议问题