11

PHPUnit/DBUnit で実際の速度の問題が発生しています。拡張するものPHPUnit_Extensions_Database_TestCaseはすべて、実行に永遠にかかります。189 個のテストがあるため、スイートの所要時間は約 8 ~ 9 分です。せいぜい30秒かかると思っていました;-)

データベースを初期状態に戻すには時間がかかるようです。そのため、データセットをできるだけ小さくし、各テスト ケースに必要なテーブルの数を制限しています。什器を使用し、可能な限り共有しています。

実行を高速化するために使用できる設定や変更はありますか? テスト全体で MySQL サーバーが行っていることを見ると、多くの切り捨て/挿入が行われているように見えますが、テスト データ セットを一時テーブルにパックし、各テストで単純にそれらを選択する方が確かに高速でしょうか?

私が使用しているドライバーは、XML テスト データセットを含む PDO/MySQL です。

4

2 に答える 2

21

グーグルで検索すると、所要時間が 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 を使用することをお勧めします。

于 2012-05-02T09:48:40.483 に答える
1

DbUnit でのフィクスチャの作成は非常に遅いです。core2duo e8400 4gb kingston 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 をオーバーライドできます。この例では、すべてのテスト ケースで 1 つの pdo 接続のみを使用します (依存性注入を使用してコードに注入できます)。セットアップ操作をオーバーライドすることで、テストケースごとに 1 回だけ、またはテストごとに 1 回だけフィクスチャを設定できます (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.)

プロジェクトごとに 1 回だけフィクスチャを作成し、その後トランザクションですべてのテストを使用し、すべてのテスト後にロールバックする別のソリューション。(制約をチェックするためにコミットする必要がある pgsql 遅延コードがある場合、これは機能しません。)

于 2013-07-11T20:13:52.707 に答える