Решение проблемы расхода памяти PHPUnit & Zend_Test для Zend Framework веб-приложения

// Апрель 12th, 2011 // PHP, Zend Framework, Веб-разработка, Методологии разработки

На работе часть команды занимается написанием тестов для веб-приложения на Zend Framework. Надо сказать, что фреймворк довольно тяжелый, а уж в режиме тестов тем более (т.к. один процесс в этом режиме обрабатывает не один HTTP-запрос, как в обычном режиме, а целую кучу). Раньше всё было хорошо, но постепенно тестов становилось всё больше и больше, они начали интенсивно кушать память и в один прекрасный момент перестали работать совсем.Мы увеличивали лимиты (memory_limit) но потом они стали совсем уж большими (512Мб). В системе тестирования то и дело вываливались ошибки типа этой:

Allowed memory size of 33554432 bytes exhausted.

Сокращение расхода памяти в phpUnit

Поэтому стало ясно, что надо что-то менять. Погуглив данную тему, я узнал о специальном режиме работы PHPUnit — «processIsolation». Смотрим в мануалку:

—process-isolation Run each test in a separate PHP process.

Ага, каждый тест запускается в отдельном процессе. А значит, он уж точно влезет в лимит ( ну если конечно мы не сделаем мега-тест на пол-гигабайта :-) ). Чтобы активировать этот режим нужно либо добавить параметр при запуске phpunit (см. выше), либо дописать его в phpunit.xml файл.

<?xml version=»1.0″ encoding=»UTF-8″ ?>
<phpunit bootstrap=»./application/bootstrap.php»
colors=»true»
convertErrorsToExceptions=»true»
convertNoticesToExceptions=»true»
convertWarningsToExceptions=»true»
processIsolation=true»
stopOnFailure=»true»
syntaxCheck=»true»>

Запустили его, и теперь расход памяти уменьшился, а тесты стали проходить.

Note1: Текущее состояние (currentState) в режиме изоляции процессов phpUnit

Тут надо отметить, что в режиме работы с изоляцией процессов могут происходить некоторые проблемы с инклюдами. Это связано с параметром $this->preserveGlobalState класса контроллера. Вот что видно в исходниках:</p> <pre class=»> $template->setVar(
array(

‘included_files’ => $this->preserveGlobalState ? PHPUnit_Util_GlobalState::getIncludedFilesAsString() : »,
‘constants’ => $this->preserveGlobalState ? PHPUnit_Util_GlobalState::getConstantsAsString() : »,
‘globals’ => $this->preserveGlobalState ? PHPUnit_Util_GlobalState::getGlobalsAsString() : »,

)
);

А т.к. переменная preserveGlobalState по-умолчанию true:

    /**
     * Whether or not this test should preserve the global state when running in a separate PHP process.
     *
     * @var    boolean
     */
    protected $preserveGlobalState = TRUE;

то нам надо перопределить её.

class MyTestCase extends PHPUnit_Framework_TestCase
{
    public function run(PHPUnit_Framework_TestResult $result = NULL)
    {
        $this->setPreserveGlobalState(false);
        return parent::run($result);
    }
}

Однако, у меня, вс1 было с точностью наоборот. Т.е. проблемы с инклюдами возникли после становки в false этого параметра.

Note2: Циклические ссылки и тесты

При наличии циклических ссылок, вас спасет флаг —no-globals-backup . Он отключает бэкап+рестор «глобальных» переменных при запуске теста. Опасно, но помогает при наличии в программе случайных циклических ссылок — как в PEAR, например.

Литература

http://stackoverflow.com/questions/4216027/phpunit-with-zend-framework-memory-problem
http://matthewturland.com/2010/08/19/process-isolation-in-phpunit/

http://friendfeed.com/dklab/fc835155/phpunit-php-fatal-error-allowed-memory-size-of

Share

Спасибо!


Если вам помогла статья, или вы хотите поддержать мои исследования и блог - вот лучший способ сделать это:


Комментировать