Решение проблемы расхода памяти PHPUnit & Zend_Test для Zend Framework веб-приложения
// 12 апреля, 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
Спасибо!
Если вам помогла статья, или вы хотите поддержать мои исследования и блог - вот лучший способ сделать это: