Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rollback transactions when doing functional testing with Symfony2

I'm trying to write a functional test for my project in Symfony2. I'd like to test if a user can access a page, fill up a form and submit it. I'm trying to find a way to rollback the database to the state it was before the test. I found a helper class I slightly modified at https://gist.github.com/Vp3n/5472509 which extends WebTestCase and overload setUp and tearDown methods. Here are the mods I did in order to try to make it works:

 /**
 * Before each test we start a new transaction
 * everything done in the test will be canceled ensuring isolation et speed
 */
protected function setUp()
{
    parent::setUp();
    $this->client = $this->createClient();
    $this->em = static::$kernel->getContainer()
        ->get('doctrine')
        ->getManager();
    $this->em->getConnection()->beginTransaction();
    $this->em->getConnection()->setAutoCommit(false);
}
/**
 * After each test, a rollback reset the state of 
 * the database
 */
protected function tearDown()
{
    parent::tearDown();
    if($this->em->getConnection()->isTransactionActive()){
        echo 'existing transactions';
        $this->em->getConnection()->rollback();
        $this->em->close();
    }
}

When I run the tests, it acknowledges for existing transactions but the rollback fails and modifications are persisted.

Test Logs:

Runtime:       PHP 5.6.15

.existing transactions.      2/2 (100%)existing transactions                                                                                      

Time: 5.47 seconds, Memory: 24.50MB

OK (2 tests, 5 assertions)

What am I doing wrong? Is that even the best practice?

EDIT

This worked for me:

abstract class DatabaseWebTest extends WebTestCase {
    /**
     * helper to acccess EntityManager
     */
    protected $em;
    /**
     * Helper to access test Client
     */
    protected $client;
    /**
     * Before each test we start a new transaction
     * everything done in the test will be canceled ensuring isolation et speed
     */
    protected function setUp()
    {
        parent::setUp();
        
        $this->client = $this->createClient(['environment' => 'test'], array(
            'PHP_AUTH_USER' => 'user',
            'PHP_AUTH_PW'   => 'password',
            ));
        $this->client->disableReboot();
        $this->em = $this->client->getContainer()->get('doctrine.orm.entity_manager');        
        $this->em->beginTransaction();
        $this->em->getConnection()->setAutoCommit(false);
    }
    /**
     * After each test, a rollback reset the state of 
     * the database
     */
    protected function tearDown()
    {
        parent::tearDown();

        if($this->em->getConnection()->isTransactionActive()) {
            $this->em->rollback();
        }        
    }
}
like image 387
Etienne Avatar asked Feb 17 '17 10:02

Etienne


3 Answers

Are you doing more than one request with the Client? If so, you problem might be that the client shutdowns the kernel after one request was performed. But you can disable that with $this->client->disableReboot() So this snippet snippet should be idempotent:

public function setUp()
{
    $this->client = $this->createClient(['environment' => 'test']);
    $this->client->disableReboot();
    $this->em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
    $this->em->beginTransaction();
}

public function tearDown()
{
    $this->em->rollback();
}

public function testCreateNewEntity()
{
    $this->client->request('GET', '/create/entity/form');
    $this->client->request('POST', '/create/entity/unique/123');
}
like image 120
leberknecht Avatar answered Sep 29 '22 00:09

leberknecht


I would recommend to use this bundle: https://packagist.org/packages/dama/doctrine-test-bundle

Its very easy to setup and will roll-back any database changes automatically after every single testcase. No need to implement any custom things.

like image 45
dmaicher Avatar answered Sep 29 '22 01:09

dmaicher


You could use a dedication test database for testing

Another option is using Codeception. This is a unit testing bundle that works with Symfony. If you use this, you can configure it to use a test database and then "clean it up" after each testing cycle.
An example yaml configuartion to do that would be something like, it is the cleanup: true that does what you want;

class_name: UnitTester
modules:
    enabled:
        - Asserts
        - Symfony2:
            app_path: '../../app'
            var_path: '../../app'
            environment: 'test'
        - Doctrine2:
            depends: Symfony2
            cleanup: true
        - \AppBundle\Helper\Unit
like image 42
Rooneyl Avatar answered Sep 29 '22 00:09

Rooneyl