Как покрыть тестами устаревший код?
Многим разработчикам приходилось поддерживать и дорабатывать устаревшие приложения, в которых никогда не было автотестов. С помощью приемочных тестов библиотеки Codeception можно покрыть
Идея приемочных тестов в том, что приложение тестируется целиком, как есть. Для
Я расскажу на примере S2, как добавлять приемочные тесты в
Проще всего Codeception подключить к проекту через composer:
"require-dev": {
"codeception/codeception": "^4.2",
"codeception/module-asserts": "^2.0.0",
"codeception/module-phpbrowser": "^2.0"
}
В проекте нужно создать файл codeception.yml:
suites:
acceptance:
actor: AcceptanceTester
modules:
enabled:
- Asserts
- PhpBrowser:
url: http://localhost:8881
curl:
CURLOPT_TIMEOUT_MS: 120000
После этого файлы тестов можно писать в таком
<?php
use Codeception\Example;
class InstallCest
{
public function tryToTest(AcceptanceTester $I, Example $example): void
{
$I->install('admin', 'passwd', $example['db_type'], $example['db_user'], $example['db_password']);
$I->amOnPage('/');
$I->see('Site powered by S2');
$I->click(['link' => 'Page 1']);
$I->see('If you see this text, the install of S2 has been successfully completed.');
$I->canWriteComment();
}
}
Здесь методы amOnPage()
, see()
, click()
— встроенные, а install()
и canWriteComment()
— мои сокращения, определенные в
<?php
use Codeception\Actor;
class AcceptanceTester extends Actor
{
public function install(string $userName, string $userPass, string $dbType, string $dbUser, string $dbPassword): void
{
$I = $this;
$I->amOnPage('/');
$I->seeLink('install S2', '/_admin/install.php');
$I->amOnPage('/_admin/install.php');
$I->seeResponseCodeIs(200);
$I->see('S2 2.0dev', 'h1');
$I->selectOption('req_db_type', $dbType);
$I->fillField('req_db_host', '127.0.0.1'); // not localhost for Github Actions
$I->fillField('req_db_name', 's2_test');
$I->fillField('db_username', $dbUser);
$I->fillField('db_password', $dbPassword);
$I->fillField('req_username', $userName);
$I->fillField('req_password', $userPass);
$I->click('start');
$I->canSeeResponseCodeIs(200);
$I->see('S2 is completely installed!');
}
public function canWriteComment(): void
{
$I = $this;
$name = 'Roman 🌞';
$I->fillField('name', $name);
$I->fillField('email', 'roman@example.com');
$I->fillField('text', 'This is my first comment! 👪🐶');
$text = $I->grabTextFrom('p#qsp');
preg_match('#(\d\d)\+(\d)#', $text, $matches);
$I->fillField('question', (int)$matches[1] + (int)$matches[2]);
$I->click('submit');
$I->seeResponseCodeIs(200);
$I->see($name . ' wrote:');
$I->see('This is my first comment!');
}
}
Теперь посмотрим, как это всё запускается. Я написал отдельный скрипт:
# Очистка тестовой базы данных
mysql -uroot --execute="DROP DATABASE IF EXISTS s2_test; CREATE DATABASE s2_test;"
# Запуск веб-сервера
APP_ENV=test \
PHP_CLI_SERVER_WORKERS=2 \
nohup php \
-d "max_execution_time=-1" \
-d "opcache.revalidate_freq=0" \
-S localhost:8881 >/dev/null 2>&1 &
serverPID=$!
# Запуск тестов
php _vendor/bin/codecept run acceptance
pkill -P $serverPID # Убиваем воркеры PHP, образовавшиеся из-за PHP_CLI_SERVER_WORKERS
kill $serverPID
Перед запуском тестов поднимается встроенный в php http://localhost:8881/
. PHP_CLI_SERVER_WORKERS=2
), так как движок в процессе установки обращается сам к себе, чтобы понять, какая схема перезаписи URL доступна. В процессе установки создается файл config.php
. Чтобы PHP сразу видел изменения в этом файле, пришлось переопределить параметр из php.ini: opcache.revalidate_freq=0
. Альтернатива — добавить sleep(), но я не хотел играться с ненадежными способами. Переменная окружения APP_ENV=test
говорит движку, чтобы он вместо файла config.php
создавал и использовал файл config.test.php
. Это упрощает запуск и тестов и обычной версии для разработки из одной папки.
Достоинства получившегося способа написания приемочных тестов следующие. Устаревший код приложения практически не нужно дорабатывать, чтобы писать тесты. Так как приложение тестируется через HTTP API, внутренние изменения в приложении, не меняющие API, не требуют доработки тестов. Тесты запускаются где угодно, я даже добавил запуск тестов в github actions при каждом пуше веток.
Недостатки, как обычно, есть продолжения достоинств. Тесты покрывают только серверную часть,
Оставьте свой комментарий