Данное решение подойдет только для тех, кто разбил тесты на модули, а не тестирует сразу весь проект.
Когда ваш проект большой и все ООП, то это кушает много памяти. А вы еще, как на зло, заюзали некогда очень популярный среди разработчиков на PHP - Codeception
. Я не буду здесь критиковать Codeception
, личный выбор каждого, но, ИМХО, если встанет выбор между Codeception
и PHPUnit
, то выбирайте второе. ;) Но, здесь мы собрались по другой причине...
Причина написания данной статьи является выход таймаута или переполнение памяти при запуске тестов, что никак не решается в Codeception
из коробки, но прекрасно решается в PHPUnit
.
Но, все решаемо и я поделюсь с вами небольшим куском кода, который вы можете доработать, или использовать, как есть.
Для работы над понадобятся следующие пакеты:
- symfony/process ^3.3
- symfony/yaml ^3.3
- zendframework/zend-config ^3.1
А также, Composer
и codeception.yml
в корне проекта, где перечислены модули для тестирования в секции include
.
Создадим файл /bin/iso-test
с таким содержанием:
#!/usr/bin/env php
<?php
require_once __DIR__ '/../vendor/autoload.php';
use Symfony\Component\Process\Process;
use Symfony\Component\Yaml\Yaml as YamlDecoder;
use Zend\Config\Reader\Yaml as YamlConfig;
$codeceptionConfig = (new YamlConfig([YamlDecoder::class, 'parse']))->fromFile(__DIR__ . '/../codeception.yml');
$beginTime = new DateTime();
$totalMemory = [];
$k = count($codeceptionConfig['include']);
$i = 1;
foreach ($codeceptionConfig['include'] as $name) {
echo colorStr(sprintf("(%d/%d) Wait...\n", $i++, $k), 'blue');
$process = new Process(
__DIR__ . '/../vendor/bin/codecept run --ansi -c ' . $name,
null,
null,
null,
300
);
$process->start();
$process->wait(function ($type, $buffer) {
echo $buffer;
});
if (!$process->isSuccessful()) {
echo $process->getErrorOutput();
exit($process->getExitCode());
}
$totalMemory[] = fetchMemory($process->getOutput());
echo PHP_EOL;
}
echo colorStr('Done!', 'green') . PHP_EOL;
echo colorStr(
sprintf(
'Time: %s, Memory: %.2fMB',
(new DateTime())->diff($beginTime)->format('%H:%I:%S'),
calcMemory($totalMemory)
),
'cyan'
) . PHP_EOL;
function fetchMemory(string $output): array {
$matches = [];
preg_match('/Memory: (\d+(\.\d+|)(\w+))/', $output, $matches);
return [$matches[3], (float)$matches[1]];
}
function calcMemory(array $totalMemory): float {
$result = [];
foreach ($totalMemory as $memory) {
switch ($memory[0]) {
case 'GB':
$result[] = $memory[1] * 1024;
break;
case 'MB':
$result[] = $memory[1];
break;
case 'KB':
$result[] = $memory[1] / 1024;
break;
default:
throw new DomainException('Fatal error while calculation total memory');
}
}
return array_sum($result) / count($result);
}
function colorStr($str, $code)
{
return sprintf('%s[%dm%s%s[0m', chr(27), getColorCode($code), $str, chr(27));
}
function getColorCode($name)
{
$styles = [
'red' => 31,
'green' => 32,
'yellow' => 33,
'blue' => 34,
'magenta' => 35,
'cyan' => 36,
'white' => 37,
];
return $styles[$name];
}
Файл codeception.yml
должен содержать секцию include
, т.к. именно по этой секции мы и запускаем наши тесты изолированно:
include:
- module/Application
- module/Amazon
# и еще куча других модулей
paths:
output: _codeceptOutput
По результата запуска php bin/iso-test
вы можете получить такой вывод в случае успеха:
Done!
Time: 00:13:01, Memory: 49.58MB
, где указано общее время выполнения всех тестов и среднее значение использованной памяти. Если во время выполнения произошла ошибка и тест не прошел, то процесс будет прерван.
Данное решение хорошо спасает, т.к. аналогично запущенные тесты просто через vendor/bin/codecept run
потребовали бы минимум 1024Мб
свободной оперативной памяти и данные число постоянно бы росло, как на дрожжах.