How can I speed up the PHPUnit + DBUnit test package?

I am working with some high speed issues using PHPUnit / DBUnit. Everything that extends to PHPUnit_Extensions_Database_TestCase runs forever. With 189 trials, the package takes about 8-9 minutes. I was hoping it would take no more than 30 seconds; -)

It seems that restoring the database to its original state is a process that takes time, so we made as few of our data sets as possible and limited the number of tables required for each test case. I use appliances and shares as much as possible.

Are there any settings or modifications that I can use to speed up execution? Looking at what the MySQL server does during the tests, it seems that a lot of truncation / insertion is occurring, but, of course, would it be faster to pack the test data sets into temporary tables and then just select them for each test?

I am using a PDO / MySQL driver with an XML test dataset.

+8
php unit-testing pdo phpunit dbunit
source share
2 answers

After Googling, I was able to reduce the time spent by 10 minutes to 1 minute. It turns out that changing some InnoDB configuration settings in my.ini / my.cnf will help.

Setting innodb_flush_log_at_trx_commit = 2 seems to do the job. After changing it, restart the MySQL server.

More about dev.mysql.com: innodb_flush_log_at_trx_commit

The setting controls how the ACID corresponds to flushing logs. The default value is 1, which corresponds to the full ACID, which means

The log buffer is written to the log file each time a transaction is committed, and the flash drive operation is performed in the log file.

With a value of 2, the following occurs:

The log buffer is written to the file each time it commits, but the flush to disk operation is not performed on it.

The key difference here is that since the log is not written at every commit, the operating system may crash or power outage may occur. For production, stick to 1. For local development with a test database, a value of 2 must be safe.

If you are working with data that will be transferred to a live database, I would suggest sticking to a value of 1.

+18
source share

Creating a device in DbUnit is extremely slow. It takes 1.5 seconds each time with the core2duo e8400 4gb kingston 1333. You can find the bottleneck with xdebug and fix it (if you can), or you can do one of the following:

one).

You can only run the files that you are currently developing using the custom xml bootstrap:

 <?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://phpunit.de/phpunit.xsd" backupGlobals="false" verbose="true" bootstrap="test/bootstrap.php"> <testsuites> <testsuite> <directory>test/integration</directory> <exclude>test/integration/database/RoleDataTest.php</exclude> </testsuite> </testsuites> <php> <env name="APPLICATION_MODE" value="test"/> </php> </phpunit> 

The exclusive part is important here. You can also use test groups.

2.)

 namespace test\integration; abstract class AbstractTestCase extends \PHPUnit_Extensions_Database_TestCase { static protected $pdo; static protected $connection; /** * @return \PHPUnit_Extensions_Database_DB_IDatabaseConnection */ public function getConnection() { if (!isset(static::$pdo)) { static::$pdo = new \PDO('pgsql:host=localhost;port=5432;dbname=dobra_test', 'postgres', 'inflames', array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION)); static::$connection = $this->createDefaultDBConnection(static::$pdo); } return static::$connection; } /** * @return \PHPUnit_Extensions_Database_Operation_DatabaseOperation */ static protected $fixtureSet = false; protected function getSetUpOperation() { $c = get_class($this; if (!$c::$fixtureSet) { $c::$fixtureSet = true; return \PHPUnit_Extensions_Database_Operation_Factory::CLEAN_INSERT(true); } return \PHPUnit_Extensions_Database_Operation_Factory::NONE(); } static protected $dataSet; /** * @return \PHPUnit_Extensions_Database_DataSet_IDataSet */ public function getDataSet() { $c = get_class($this; if (!isset($c::$dataSet)) { $c::$dataSet = $this->createDataSet(); } return $c::$dataSet; } /** * @return \PHPUnit_Extensions_Database_DataSet_IDataSet */ abstract protected function createDataSet(); protected function dataSetToRows($tableName, array $ids) { $transformer = new DataSetRowsTransformer($this->getDataSet()); $transformer->findRowsByIds($tableName, $ids); $transformer->cutColumnPrefix(); return $transformer->getRows(); } } 

You can override TestCase. In this example, you will use only one pdo connection for each test scenario (you can enter it into your code using dependency injection), by overriding the tuning operation, you can install the device only once for each test or only once for each test ( depends on self:: or $cls = get_class($this); $cls:: . (PHPUnit has a poor design, it creates a new instance with each test call, so you need to crack class names to store variables for each instance or for each class.) In this scenario, you need to write tests so that they depend on each other using @depend annotation. For example, you can delete the same line that you created in the previous test.

With this test code, 1.5 secs instead of 6 x 1.5 = 9 secs :

 namespace test\integration\database; use Authorization\PermissionData; use test\integration\AbstractTestCase; use test\integration\ArrayDataSet; class PermissionDataTest extends AbstractTestCase { static protected $fixtureSet = false; static protected $dataSet; /** @var PermissionData */ protected $permissionData; /** * @return \PHPUnit_Extensions_Database_DataSet_IDataSet */ public function createDataSet() { return new ArrayDataSet(array( 'permission' => array( array('permission_id' => '1', 'permission_method' => 'GET', 'permission_resource' => '^/$'), array('permission_id' => '2', 'permission_method' => 'POST', 'permission_resource' => '^/$'), array('permission_id' => '3', 'permission_method' => 'DELETE', 'permission_resource' => '^/$') ), 'user' => array( array('user_id' => '1', 'user_name' => 'Jánszky László', 'user_email' => 'a@b.d', 'user_salt' => '12435') ), 'user_permission' => array( array('user_permission_id' => '1', 'user_id' => '1', 'permission_id' => '1'), array('user_permission_id' => '2', 'user_id' => '1', 'permission_id' => '2') ), 'role' => array( array('role_id' => '1', 'role_name' => 'admin') ), 'role_permission' => array( array('role_permission_id' => '1', 'role_id' => '1', 'permission_id' => '1') ), 'permission_cache' => array( array('permission_cache_id' => '1', 'user_id' => '1', 'permission_id' => '1'), array('permission_cache_id' => '2', 'user_id' => '1', 'permission_id' => '2'), ) )); } public function testReadAllShouldReturnEveryRow() { $this->assertEquals($this->permissionData->readAll(), $this->dataSetToRows('permission', array(3, 2, 1))); } /** @depends testReadAllShouldReturnEveryRow */ public function testReadAllByRoleIdShouldReturnEveryRowRelatedToRoleId() { $this->assertEquals($this->permissionData->readAllByRoleId(1), $this->dataSetToRows('permission', array(1))); } /** @depends testReadAllByRoleIdShouldReturnEveryRowRelatedToRoleId */ public function testReadAllByUserIdShouldReturnEveryRowRelatedToUserId() { $this->assertEquals($this->permissionData->readAllByUserId(1), $this->dataSetToRows('permission', array(2, 1))); } /** @depends testReadAllByUserIdShouldReturnEveryRowRelatedToUserId */ public function testCreateShouldAddNewRow() { $method = 'PUT'; $resource = '^/$'; $createdRow = $this->permissionData->create($method, $resource); $this->assertTrue($createdRow['id'] > 0); $this->assertEquals($this->getDataSet()->getTable('permission')->getRowCount() + 1, $this->getConnection()->getRowCount('permission')); return $createdRow; } /** @depends testCreateShouldAddNewRow */ public function testDeleteShouldRemoveRow(array $createdRow) { $this->permissionData->delete($createdRow['id']); $this->assertEquals($this->getDataSet()->getTable('permission')->getRowCount(), $this->getConnection()->getRowCount('permission')); } /** @depends testDeleteShouldRemoveRow */ public function testDeleteShouldRemoveRowAndRelations() { $this->permissionData->delete(1); $this->assertEquals($this->getDataSet()->getTable('permission')->getRowCount() - 1, $this->getConnection()->getRowCount('permission')); $this->assertEquals($this->getDataSet()->getTable('user_permission')->getRowCount() - 1, $this->getConnection()->getRowCount('user_permission')); $this->assertEquals($this->getDataSet()->getTable('role_permission')->getRowCount() - 1, $this->getConnection()->getRowCount('role_permission')); $this->assertEquals($this->getDataSet()->getTable('permission_cache')->getRowCount() - 1, $this->getConnection()->getRowCount('permission_cache')); } public function setUp() { parent::setUp(); $this->permissionData = new PermissionData($this->getConnection()->getConnection()); } } 

3).

Another solution is to create a fixture only once for each project, and after that use each test in transactions and rollback after each test. (This does not work if you have pgsql pending code that needs to be committed to check for constraints.)

+1
source share

All Articles