As the title, my goal is to rollback any commit made during Behat functional tests. I checked this answer very similar, but it is from two years ago and it seems impossible to do.
Maybe with Behat 3 now it's possible.
I know that with PHPUnit I can reach something like that using startUp and tearDown methods.
I tried to start and rollback a transaction hooking with the @BeforeScenario and @AfterScenario annotations, but it seems that behat and the application doesn't share the same instance of entity manager.
Some advice on it?
Thank you.
Thank you all for your advices. Here some new considerations:
LOAD FIXTURES: Yes, it works. I'm able to run fixtures before my tests starts, but the problem (my fault to not mention it) is that fixtures sometimes needs several minutes to load and it is annoying to wait 10 or more minutes before your tests starts.
BEGIN/ROLLBACK TRANSACTION: It works too or it seems to be. I receive no errors, but the data written during tests is still in my database when they ended. I added the first in a method tagged @BeforeScenario e the latter in a method tagged with @AfterScenario
$this->kernel->getContainer() ->get('doctrine.orm.entity_manager') ->getConnection() ->beginTransaction(); $this->kernel->getContainer() ->get('doctrine.orm.entity_manager') ->getConnection() ->rollBack();
public function gatherContexts(BeforeScenarioScope $scope) { $environment = $scope->getEnvironment(); $connection = $this->kernel->getContainer()->get('doctrine.orm.entity_manager')->getConnection(); $connection->beginTransaction(); $connection->createSavepoint('tests'); } public function rollback(AfterScenarioScope $scope) { $connection = $this->kernel->getContainer()->get('doctrine.orm.entity_manager')->getConnection(); $connection->rollbackSavepoint('tests'); }
All these tests are used to test my API REST project. After these considerations I think that Behat and my application doesn't share the same instance of the entity manager. Are you able to share the same exactly instance between your tests and your projects during tests?
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With