Как избавиться от ошибки ‘Cannot redeclare class’ при выполнение теста PHPUnit? Разберем причины и способы устранения.
Ошибка подобного рода может возникать когда при выполнении набора тестов один из классов определяется более 1 раза. Такое может возникнуть при выполнении интеграционных тестов.
Рассмотрим пример простого интеграционного теста на Zend Framework:
public function testValidateUserSuccess()
{
$this->getRequest()
->setMethod('POST')
->setPost(array('username' => 'test', 'msisdn' => '375291234567'));
$this->dispatch('/api/user/validate');
$this->assertResponseCode(200);
}
public function testValidateUserFailure()
{
$this->getRequest()
->setMethod('POST')
->setPost(array('username' => 'test', 'msisdn' => 'abc'));
$this->dispatch('/api/user/validate');
$this->assertResponseCode(400);
}
При выполнении данного набора тестов приложение будет запущено дважды, а значит дважды ининицализируются все методы Bootstrap‘а и Autoloader.
Один из вариантов решения — это запуск теста в отдельном процессе. Для этого необходимо добавить перед тестом @runInSeparateProcess
.
/**
* @runInSeparateProcess
*/
public function testValidateUserSuccess()
{
...
}
/**
* @runInSeparateProcess
*/
public function testValidateUserFailure()
{
...
}
Можно запустить каждый тест в отдельном процессе, указав перед классом теста @runTestsInSeparateProcesses
/**
* @runTestsInSeparateProcesses
**/
class UserTest extends PHPUnit_Framework_TestCase
{
public function testValidateUserSuccess()
{
...
}
public function testValidateUserFailure()
{
...
}
}
Подводные камни
Во время запуска тестов я периодически ловил ошибку
RuntimeException: PHP Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in -:36
Полный текст ошибки
PHPUnit 3.6.10 by Sebastian Bergmann.
Configuration read from /path/to/project/tests/phpunit.xml
E
Time: 0 seconds, Memory: 4.50Mb
There was 1 error:
1) UserTest::testValidateUserSuccess
RuntimeException: PHP Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in -:36
Stack trace:
#0 -(36): serialize(Array)
#1 -(135): __phpunit_run_isolated_test()
#2 {main}
thrown in - on line 36
Гуглил, нашел пару предположений:
- PHPUnit пытается все сложить в переменную
$GLOBALS
и ошибка возникает когда в тесте есть замыкания. Фикс: не использовать замыкания. Не мой вариант — у меня замыканий не было. - Причина
@runInSeparateProcess
. Фикс: не используйте@runInSeparateProcess
. Однозначно тоже не мой вариант.
Не сумев найти решения на просторах интернета, я решил обновить PHPUnit с 3.6.10 => 4.7.7 — помогло 🙂
P.S. совместимость Zend Framework 1.12.13 с PHPUnit 4.7 нормальная (несмотря на заявленную поддержку 3.7).