簡體   English   中英

如何加快PHPUnit + DBUnit測試套件的執行速度?

[英]How can I speed up my PHPUnit + DBUnit test suite execution?

我正在使用PHPUnit / DBUnit來解決一些真正的速度問題。 任何擴展PHPUnit_Extensions_Database_TestCase東西都需要永遠運行。 通過189次測試,該套件大約需要8-9分鍾。 我有點希望最多需要30秒;-)

看起來將數據庫恢復到其初始狀態是花費時間的過程,因此我們使數據集盡可能小並限制每個測試用例所需的表數。 我正在使用燈具並盡可能地分享。

我可以使用任何設置或修改來加快執行速度嗎? 看看MySQL服務器在整個測試過程中做了什么,似乎發生了大量的截斷/插入,但是將測試數據集打包到臨時表中然后只需為每個測試選擇它們會更快嗎?

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

通過谷歌搜索,我設法將所需的時間從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。

DbUnit中的夾具創建非常慢。 使用core2duo 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連接(你可以通過依賴注入將它注入你的代碼),通過覆蓋設置操作,你可以在每個測試用例中只設置一次fixture,或者每次測試只設置一次(取決於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' => '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.)

另一種解決方案是每個項目只創建一次夾具,然后在每次測試后使用事務中的每個測試和回滾。 (如果你有pgsql延遲代碼需要提交檢查約束,這不起作用。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM