How to rollback commits in Behat 3 functional tests with Symfony2 and Doctrine? How to rollback commits in Behat 3 functional tests with Symfony2 and Doctrine? symfony symfony

How to rollback commits in Behat 3 functional tests with Symfony2 and Doctrine?


If your context implements the KernelAwareContext, then in the @BeforeScenario and @AfterScenario annotated methods, you can do

$this->kernel->getContainer()->getDoctrine()->getConnection()->beginTransaction();$this->kernel->getContainer()->getDoctrine()->getConnection()->rollBack();

This is assuming you only have one connection and it's used by the em.

You can also try $connection->setRollbackOnly() but bear in mind that it will wildly depend on your underlying db. Mysql might autocommit in quite a few case were your didn't expected it.

And lastly there is also $connection->createSavepoint('savePointName') to use with $connection->rollbackSavepoint('savePointName')

This is out of my head so it might needs some adjustments.


The problem is that Behat uses the client from the Browser-Kit, so you are suffering from rebootable client.Luckily the Symfony2 extension fetches the client from the container, so we can override it. Here is what did the trick for me:

Create a wrapper class:

class NoneRebootableClient extends Client{    public function __construct(KernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null)    {        parent::__construct($kernel, $server, $history, $cookieJar);        $this->disableReboot();    }    public function setServerParameters(array $parameters)    {        return;    }}

The setServerParameters override is only required if you have something like "Before each scenario, set an auth header". As Behat resets the headers after each call in vendor/behatch/contexts/src/HttpCall/Request/BrowserKit.php:resetHttpHeaders we would loose the auth header after the first call. Note: this might have unexpected side-effects.

Then configure the service for the test-env (the test.client id is important, its the id that behat uses for the client lookup):

test.client:    class: App\Tests\NoneRebootableClient    public: true    arguments:        - '@kernel'        - []        - '@test.client.history'        - '@test.client.cookiejar'

And then use the Before/After Scenario in your context as you already described above:

/** * @BeforeScenario */public function startTransaction($event){    $this->doctrine->getConnection()->beginTransaction();}/** * @AfterScenario */public function rollbackTransaction($event){    $this->doctrine->getConnection()->rollBack();}

Et voilĂ , we are idempotent :)


With setUp method, you can begin a transaction. You can rollback it in tearDown method IF there is no commit between the two method calls.

It's very dangerous to launch tests on a production database even if you rollback queries. It's a better way to initialize a database test with data fixture. If you can not do it (I think so), you want to test with production data. Use doctrine:migrations (or doctrine:schema:create) to copy your production database schema into your dev environnement and add a script to copy data.