Как ускорить выполнение набора тестов PHPUnit + DBUnit?

Я запускаю некоторые проблемы с реальной скоростью с PHPUnit / DBUnit. Все, что выходит PHPUnit_Extensions_Database_TestCase требуется вечность для запуска. С 189 тестов, люкс занимает около 8-9 минут. Я надеялся, что это займет не более 30 секунд ; -)

похоже, что восстановление базы данных в исходное состояние, процесс, который занимает время, поэтому мы сделали наши наборы данных как можно меньше и ограничить количество столов мы требуем для каждого теста. Я использую светильники и делюсь как можно больше по возможности.

есть ли какие-либо настройки или изменения, которые я могу использовать для ускорения выполнения? Глядя на то, что сервер MySQL делает во время тестов, кажется, что происходит много усечения/вставки, но, конечно, было бы быстрее упаковать наборы тестовых данных во временные таблицы, а затем просто выбрать их для каждого теста?

драйвер, который я использую, - это PDO / MySQL с тестовым набором данных XML.

2 ответов


после Googling мне удалось сократить время, которое требуется от 10 минут до 1 минуты. Оказывается, изменение некоторых параметров конфигурации InnoDB в моем.Ини/мой.cnf поможет.

задание innodb_flush_log_at_trx_commit = 2 кажется, делает свою работу. После его изменения перезапустите сервер MySQL.

на dev.mysql.com: innodb_flush_log_at_trx_commit

параметр определяет, насколько ACID соответствует промывке журналов. Значение по умолчанию-1 полное кислотное соответствие, что означает

буфер журнала записывается в файл журнала при каждой фиксации транзакции, а операция flush to disk выполняется в файле журнала.

при значении 2 происходит следующее:

буфер журнала записывается в файл при каждой фиксации, но операция flush to disk на нем не выполняется.

ключевое отличие здесь в том, что журнал не записывается при каждом фиксации сбой операционной системы или отключение питания могут уничтожить его. Для производства придерживайтесь значения 1. Для локальной разработки с тестовой базой данных значение 2 должно быть безопасным.

Если вы работаете с данными, которые будут переданы в базу данных live, я бы предложил придерживаться значения 1.


создание приспособления в DbUnit чрезвычайно медленно. Это занимает 1,5 секунды каждый раз с core2duo e8400 4GB kingston 1333. Вы можете найти узкое место с 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 для каждого тестового случая (вы можете ввести его в свой код с инъекцией зависимостей), переопределяя операцию установки, вы можете установить приспособление только один раз на testcase или только один раз для каждого теста (зависит от 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, который нуждается в фиксации для проверки ограничений.)