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; 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; } 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; public function getDataSet() { $c = get_class($this; if (!isset($c::$dataSet)) { $c::$dataSet = $this->createDataSet(); } return $c::$dataSet; } 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; protected $permissionData; 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))); } public function testReadAllByRoleIdShouldReturnEveryRowRelatedToRoleId() { $this->assertEquals($this->permissionData->readAllByRoleId(1), $this->dataSetToRows('permission', array(1))); } public function testReadAllByUserIdShouldReturnEveryRowRelatedToUserId() { $this->assertEquals($this->permissionData->readAllByUserId(1), $this->dataSetToRows('permission', array(2, 1))); } 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; } public function testDeleteShouldRemoveRow(array $createdRow) { $this->permissionData->delete($createdRow['id']); $this->assertEquals($this->getDataSet()->getTable('permission')->getRowCount(), $this->getConnection()->getRowCount('permission')); } 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.)