如何加快PHPUnit + DBUnit测试套件的执行速度?

时间:2012-04-30 15:14:54

标签: php unit-testing pdo phpunit dbunit

我正在使用PHPUnit / DBUnit来解决一些真正的速度问题。任何延伸PHPUnit_Extensions_Database_TestCase的东西都需要永远运行。通过189次测试,该套件大约需要8-9分钟。我有点希望最多需要30秒; - )

将数据库恢复到其初始状态似乎是花费时间的过程,因此我们使数据集尽可能小,并限制每个测试用例所需的表数。我正在尽可能地使用固定装置和分享。

我可以使用任何设置或修改来加快执行速度吗?看看MySQL服务器在整个测试过程中做了什么,似乎发生了大量的截断/插入,但是将测试数据集打包到临时表中然后只需为每个测试选择它们会更快吗?

我正在使用的驱动程序是带有XML测试数据集的PDO / MySQL。

2 个答案:

答案 0 :(得分:20)

通过谷歌搜索,我设法将所需的时间从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合规性,这意味着

  

日志缓冲区在每次事务提交时写入日志文件,并在日志文件上执行flush to disk操作。

值为2时,会发生以下情况:

  

每次提交时都会将日志缓冲区写入文件,但不会对其执行flush to disk操作。

这里的关键区别在于,由于日志未在每次提交时写出,因此操作系统崩溃或停电可能会将其清除。对于生产,坚持使用值1.对于使用测试数据库进行本地开发,值2应该是安全的。

如果您正在使用将传输到实时数据库的数据,我建议坚持使用值1。

答案 1 :(得分: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,或者每次测试只设置一次(取决于{ {1}}或self::)。 (PHPUnit设计不好,它通过每次测试调用创建新实例,所以你必须破解类名来存储每个实例或每个类的变量。)在这种情况下,你必须编写测试以依赖彼此{{{ 1}}注释。例如,您可以删除在上一个测试中创建的同一行。

通过此测试代码$cls = get_class($this); $cls::代替@depend

1.5 secs

3。)

另一个解决方案是每个项目只创建一次夹具,然后在每次测试后使用事务中的每个测试和回滚。 (如果你有pgsql延迟代码需要提交检查约束,这不起作用。)