2012-04-30 2 views
8

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

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

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

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

+0

Вам нужно метрику, где есть узкое место. Вероятно, это ускорит ваши потребности, если вы сможете издеваться над всей базой данных, поэтому вам вообще не нужно запускать dbunit. Тест должен проходить под 10-й секундой, что на самом деле значительно медленнее для теста. – hakre

+0

Поскольку вы не упомянули, используете ли вы оборудование и как можно больше? – Pradeep

+0

Я использую светильники и разделяю как можно более разумно. Есть ли способ профилировать тестового бегуна? –

ответ

18

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

Установка 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 :-(Btw Я думаю, dbunit sux, это очень медленно .. – inf3rno

1

Создания арматуры в DbUnit крайне медленно. Он занимает 1,5 сек каждый раз с Core2Duo E8400 4gb Кингстоне 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 по каждому тестовому сценарию (вы можете ввести его в свой код с помощью инъекции зависимостей), переопределив операцию настройки, вы можете установить прибор только один раз для каждого теста или только один раз для каждого теста (в зависимости от self:: или $cls = get_class($this); $cls::). (PHPUnit имеет плохую конструкцию, он создает новый экземпляр каждым тестовым вызовом, поэтому вам нужно взломать имена классов для хранения переменных для каждого экземпляра или для каждого класса.) В этом сценарии вам нужно написать тесты, зависящие от eachother, с аннотацией @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, который требует фиксации для проверки ограничений.)

Смежные вопросы