2012-04-30 117 views
8

我遇到了一些PHPUnit/DBUnit的實際速度問題。任何延伸PHPUnit_Extensions_Database_TestCase需要永遠運行。經過189次測試,套件大約需要8-9分鐘。我有點希望它最多需要30秒;-)如何加快PHPUnit + DBUnit測試套件的執行速度?

它看起來像恢復數據庫到其初始狀態是需要時間的過程,所以我們已經使我們的數據集儘可能小,並限制每個測試用例需要的表格數量。我儘可能地使用燈具和分享。

是否有任何設置或修改可用於加速執行?看看MySQL服務器在整個測試過程中所做的事情,看起來好像有很多截斷/插入事件正在發生,但是將測試數據集打包到臨時表中並且然後爲每個測試選擇它們會更快?

我使用的驅動程序是帶有XML測試數據集的PDO/MySQL。

+0

你需要衡量瓶頸在哪裏。如果你可以模擬整個數據庫,那麼它可能會加快你的需求,所以你根本不需要運行dbunit。測試應該在10秒內運行 - 對於測試來說實際上相當慢。 – hakre

+0

既然你還沒有提到,你是否在儘可能地使用燈具並共享? – Pradeep

+0

我正在儘可能合理地使用燈具和分享。有沒有一種方法可以描述測試跑步者? –

回答

18

在谷歌搜索我已經設法將時間從10分鐘減少到1分鐘。事實證明,在my.ini/my.cnf中更改一些InnoDB配置設置將有所幫助。

設置innodb_flush_log_at_trx_commit = 2似乎做的工作。改變它之後,重新啓動你的MySQL服務器。

更多關於dev.mysql.com: innodb_flush_log_at_trx_commit

設置控制ACID兼容如何將日誌的沖刷。默認值是1,它是完全符合ACID這意味着

日誌緩衝區在每個事務寫入到日誌文件提交併在日誌文件中進行沖洗到磁盤操作。

爲2的值,則發生以下情況:

日誌緩衝區在每次提交寫出到文件中,但不對其執行沖洗到磁盤操作。

這裏的關鍵區別在於,由於在每次提交時都沒有寫出日誌,操作系統崩潰或斷電可能會將其清除。對於生產,堅持1的值。對於使用測試數據庫的本地開發,2的值應該是安全的。

如果你與將要轉移到實時數據庫中的數據的工作,我建議用1

+0

非常感謝 - 這使我的時間減少了90%!奇怪的,因爲我的表是MyISAM ...也許這是內部表? – ChrisA

+2

是的,這似乎是奇蹟......只要把它放在[mysqld]部分下(不是其他地方)。 – anroots

+0

這對pgsql沒有什麼幫助:-(順便說一句,我認爲dbunit sux,它非常慢.. – inf3rno

1

值中的DbUnit夾具創作是極其緩慢堅持。它需要與酷睿2 E8400 4GB金士頓1333每次1.5秒你可以找到Xdebug的瓶頸,並修復它(如果可以的話),或者你可以做以下之一:

1)

你只能運行測試文件,您目前使用自定義XML引導發展:

<?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> 

的排除部分是很重要的位置。您也可以使用測試組。

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(); 
    } 

} 

您可以覆蓋TestCase。在這個例子中,每個測試用例只能使用一個pdo連接(可以通過依賴注入將其注入到代碼中),通過重寫設置操作,每個測試用例只能設置一次,或者每次測試只能設置一次(取決於self::$cls = get_class($this); $cls::)。 (PHPUnit的設計很糟糕,它會在每次測試調用時創建一個新實例,因此您必須使用類名來破解每個實例或每個類的變量)。通過這種情況,您必須編寫測試以依靠@depend註釋。例如,您可以刪除您在之前的測試中創建的同一行。

通過這個測試代碼1.5 secs,而不是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' => '[email protected]', '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)

另一種解決方案來創建夾具每個項目只有一次,使用後每次考試在交易和回滾後,每測試。 (如果你有需要提交檢查約束的pgsql延期代碼,這是行不通的。)