CommonContexts
CommonContexts copied to clipboard
[SymfonyDoctrineContext] Improve performance for sqlite
Anyone who used behat and sqlite can notice that
$tool->dropSchema($metadata);
$tool->createSchema($metadata);
are very slow when it's run on sqlite guys from Liip fixed this by copying filled db file ond restoring from backup inspired by https://github.com/liip/LiipFunctionalTestBundle/blob/master/Test/WebTestCase.php
this is a rough (although working) example how it could be done
use Behat\CommonContexts;
/**
* Created by JetBrains PhpStorm.
* User: wodor
* Date: 12.03.12
* Time: 15:30
*/
class FastSqliteDoctrineContext extends \Behat\CommonContexts\SymfonyDoctrineContext
{
/**
* @param \Behat\Behat\Event\ScenarioEvent|\Behat\Behat\Event\OutlineExampleEvent $event
*
* @BeforeScenario
*
* @return null
*/
public function buildSchema($event)
{
$metadata = $this->getMetadata();
$container = $this->getContainer();
$connection = current($this->getConnections());
if ($connection->getDriver() instanceOf \Doctrine\DBAL\Driver\PDOSqlite\Driver) {
$params = $connection->getParams();
$name = isset($params['path']) ? $params['path'] : $params['dbname'];
$metadatas = $this->getEntityManager()->getMetadataFactory()->getAllMetadata();
$backup = $container->getParameter('kernel.cache_dir') . '/test_' . md5(serialize($metadatas)) . '.db';
if (file_exists($backup)) {
copy($backup, $name);
return;
}
if (!empty($metadata)) {
$tool = new \Doctrine\ORM\Tools\SchemaTool($this->getEntityManager());
$tool->dropSchema($metadata);
$tool->createSchema($metadata);
}
if (isset($backup)) {
copy($name, $backup);
}
}
}
}
This , could be blended into SymfonyDoctrineContext and, of course, requires polishing and introducing an option enabling it.
I could transform this into a PR @jakzal , are you interested in this improvement ?
I don't think it's up to me to decide. I contributed SymfonyDoctrineContext but I'm not an official maintainer.
My personal opinion is that it's better to use the database you're using on production (and there's a limited number of use cases for sqlite on production...).
Also, I don't like the idea of maintaining the dump with a database structure.
If you thought of storing data dump in your sqlite file... I prefer to define fixtures in scenarios. It's better to be explicit about the application state.
@jakzal I'm surprised to see such opinion. If you've got a big system, with many related entities to test, like:
- Users with 10 or more diffrent access levels
- For each "Basic" user you must create many entities (for each module), say you have some internal company document flow system.. for each user you must create Invoices, Contracts, Contacts, and stuff like that
- now let's assume each of these entities may have a "Status", depending on which, diffrent users may perform diffrent actions
The easiest way I can think of, is to generate "for each user" one entity per status (so that all possible permutations exist), then store this as SQLLite db file, and "copy" the database before each scenario (to reset any changes), just like @wodor suggests.
Creating seperate smaller chunks of fixtures and loading them before each scenario would make tests run very slow.
@jakzal do you still hold your opinion in the comment above? if so, how would you solve case described above?
Using sqlite is unreliable.
If you care about performance of your tests you probably shouldn't use a real database in aceprtance tests anyway. Your test repositories could use flat files for example.
Anyway, the proposed solution is so specific to sqlite that it should be probably implemented as a separate context.
@jakzal if you want to perform end-to-end tests of your system, you will need to have your persistence layer involved somehow. Otherwise, this layer will not be covered.
However I agree that it should be implemented separately
So, it's better to pollute FeatureContext's with custom steps like:
<?php
/**
* @Given /I have a category "([^"]*)"/
*/
public function iHaveACategory($name)
{
$em = $this->getContainer()->get('doctrine')->getEntityManager();
$entity = new \Acme\DemoBundle\Entity\Category();
$entity->setName($name);
$em->persist($entity);
$em->flush();
}
?>
every time we want to create and test some object?
PS. I'm asking becouse I'm right now learning Behat, and I'd like to do this "the right way".. and you guys are one of active and respected symfony community members :)
Why pollute ?
IMO, having a step in your scenario saying you have a category is much more readable than relying on an implicit initial state
I see, I'll try it that way then :)