46
Тестирование в компании Дмитрий Зенович

бегун

Embed Size (px)

Citation preview

Page 1: бегун

Тестирование в компании

Дмитрий Зенович

Page 2: бегун

Начальные условияДесятки типов онлайн демонов на C++

Десятки offline скриптов на PHP и демонов на C++

веб-интерфейс

API

генераторы статистики и отчетов

Объем кода – несколько сотен тысяч строк

Page 3: бегун

Начальные условияЧто хотелось сделать:

• сократить время итерации и увеличить продуктивность разработки

• создать отдел тестирования (тестирование проводилось силами разработчиков)

• увеличить полноту документации

• оптимизировать процесс внедрения в PHP-разработке

Page 4: бегун

Часть 1

Тестирование

Page 5: бегун

Начало тестированияСтавка на автоматизированное тестирование.

Выбор в качестве фреймворка для автоматизированного тестирования PHPUnitСтандарты кодирования. Один класс в файле. Мэппинг имени класса в имя файла.

Непрерывный рефакторинг. Переписывать все непонятное, упрощать все сложное, избавляться от дублирования.

Page 6: бегун

Библиотека / ConfigurableObjectTestlib_ConfigurableObject Эмуляция массива (доступ по индексу для свойств) Свойства типизированы

/** * HTTP-method * @var string */public $method = 'GET';

Конструктор принимает на вход массив со значениями свойств

Page 7: бегун

Библиотека / ConfigurableObjectЗащищенные свойства /** * @property * @var string */ protected $readOnly; protected function _setReadOnly($value) {throw …} protected function _getReadOnly() { return 1; }

Page 8: бегун

Библиотека / ConfigurableObjectСериализация: toArray(), __toString()Сериализация в XML@serializeXmlAs node(имя)|attribute|text|cdata|none @serializeXmlAs node(имя) в заголовке класса

$obj = Testlib_ConfigurableObject::fromXml('XmlObj', $xmlString);$obj->toXml() ;

Другие виды сериализацииВнутренние форматы сериализации, JSON, HTML, …

Page 9: бегун

Библиотека / ConfigurableObject/** * @serializeXmlAs node(begun) */class Testlib_Client_Daemon_Response_Banner_Body_Begun extends Testlib_ConfigurableObject { /** * @var Testlib_Client_Daemon_Response_Banner_Body_Begun_Banner[] */ public $banner = array();}$obj = Testlib_ConfigurableObject::fromXml( 'Testlib_Client_Daemon_Response_Banner_Body_Begun', $data);

Page 10: бегун

Библиотека / MethodMockTestlib_MethodMockИзоляция тестируемого функционалаПредотвращение лишних действийПредотвращение долгих операций в тестах (БД и т.п.)Упрощение подготовки начальной конфигурации

PECL-модуль runkit (runkit_method_copy, runkit_method_redefine, runkit_method_remove, …)Перехват встроенных функций PHPПатчи runkit

Page 11: бегун

Библиотека / MethodMock$m1 = Testlib_MethodMock::interceptMethodByCode("MyClass", "myMethod", "echo '1';");$m2 = Testlib_MethodMock::mockFunctionResult('getValue', 12345, array(2,1));$m3 = Testlib_MethodMock::interceptMethodByCode("MyClass", "myMethod", "echo 'called';");$m1->getCalledArgs();$m2->getCalledResults();$m3->isCalled();$m1->countCalled();$m2->resetCallStack();$m3->revert();

Page 12: бегун

Библиотека / TcpdumpCatcherTestlib_TcpdumpCatcher$catcher = new Testlib_TcpdumpCatcher(TESTLAB_HOST, TESTLAB_USER, TESTLAB_PASS, "192.168.1.3", "8080", "eth0");

// ...

$dump = $catcher->finish();

Внутри: tcpdump, анализ собранных пакетов, склеивание с учетом фрагментирования, парсинг

Работает локально или удаленно

Page 13: бегун

Библиотека / LogCatcherTestlib_LogCatcher$catcher = new Testlib_LogCatcher("my.log", /*"192.168.1.3", "myuser", "123456“*/);

// ...

$log = $catcher->finish();

Внутри: wc -l , tail -n

Работает локально или удаленно

Page 14: бегун

Работа с базой данныхMySQL, транзакционность InnoDB, реализация вложенных транзакций

Testlib_TestDb• Zend_Db • реализация вложенных транзакций• фабрика на основе конфигураций• остановка/восстановление репликации

Очистка по расписанию

Page 15: бегун

Библиотека / DatabaseMockTestlib_DatabaseMock$dbMock = new Testlib_DatabaseMock('Cards', 'CardsForTest', false, array('CatalogTree', 'CatalogInfo')); // ...$dbMock->revert();

Внутри:Testlib_MethodMock::interceptFunction('mysql_query');runkit_constant_redefine(…)

Различные способы экранирования имен таблиц и баз учитываются

Page 16: бегун

Библиотека / DbiCleanerTestlib_DbiCleaner$id = … Testlib_DbiCleaner::add('DbName.tableName', 'id', $id);// ... Testlib_DbiCleaner::start();

Есть также очистка из командной строки

Page 17: бегун

Библиотека / FakeDaemonTestlib_FakeDaemonПодмена ответов. nginx + php. Настройка ini-файла:[:7099]serviceName="4test"port=80host="lab128"logfile="./7099-4tests.log"

Подмена ответа:Testlib_FakeDaemon::setCustomResponse('4test',$requestText, $responseText);

Page 18: бегун

Библиотека / FakeDaemon$responseText может не содержать HTTP-заголовков$requestText может быть неполным текстом запроса

Внутри подмены:Testlib_FakeDeamon_CustomResponse::fromRequestAndResponse()Создаются условия по requestMethod, handler, каждому get-параметру, по каждому заголовку, кроме «Host». Порядок полей не важен.Ответ и его условия сохраняются в базу и заносятся в DbiCleaner

Page 19: бегун

Библиотека / FakeDaemonОбработка запроса• Определение конфигурации по порту• Получение всех подмененных запросов для сервиса (в обратном порядке)• Для каждой подмены проверяются условия (И, urldecode для get-параметров)• Если подмена найдена, то возвращается она, иначе ответ ищется в кэш, если там нет, то берется ответ настоящего демона.

Позволяет тестировать программы независимо!Тест должен очищать после себя подмены.

Page 20: бегун

Создание клиента daemon’аБазовый класс (Testlib_ClientTest)Работа с конфигом демона в БД (получение, изменение)Абстрактный http-запрос к демонуАбстрактный http-запрос к демону для тестов (конфиг, лог, подмена ответов других демонов, TcpdumpCatcher, контроль изоляции демона, поиск ошибок в логе)Запуск и остановка демона, запущен ли демон, ожидание запускаrequestHandler, requestHandlerForTests (Testlib_ConfigurableObject)

Page 21: бегун

Создание клиента daemon’аНовый клиентСоздаем новый класс; прописываем в свойствах имя демона, таблицу с конфигом, «нижние» демона; реализуем методы для каждого хэндлера:/** * @param mixed<Testlib_Client_Daemon_RequestParamsForTests_Index> $params * @return Testlib_Client_Daemon_ResponseForTests_Index */ public static function requestIndexForTests($params = array()) { return self::requestHandlerForTests('index', $params); }создаем классы (Testlib_ConfigurableObject) для входных и выходных данных хэндлеров

Page 22: бегун

Создание клиента daemon’а Написание тестов демонов $params = array( 'params' => array( 'getParams' => array(...), ... ), 'tcpdump' => array(...), 'customResponses' => array( Testlib_FakeDaemon_CustomResponse::fromRequestAndResponse( 'subdaemon', '/handler', (string) (string) new Testlib_Client_Subdaemon_Response_Handler_Body(...) ), ... )); $result = Testlib_Client_Daemon::requestIndexForTests($params);

Page 23: бегун

БиблиотекаБазовые классы тестов и сьютов демонов

Testlib_TestSuite остановка и возобновление репликацииprepare/cleanup

CommonSuite (много тестов для одного запроса к демону)

Page 24: бегун

Тестирование PHPМожно использовать библиотеки PHP-разработчиковМетоды белого ящика

Написание юнит-тестов PHPБазовый клаcс Testlib_TestCase_Php Блокирование коммита БД Cтарт транзакции в setUp, откат в tearDown Очистка глобальных объектов Список sql-запросов в отчетах об ошибке Отслеживание коммитов и роллбэков Проблема parent::setUp и parent::tearDown

Page 25: бегун

Тестирование PHPTestlib_ScriptRunnerВозвращать результат, вывод, log скриптаУмеет дожидаться освобождения lock-файлаrunScript()runScriptByIncluding() удаляет константы, определенные в скрипте удаляет функции, определенные в скрипте неправильное окружение, зато есть возможность подменять методы, базы, откатывать транзакцию

Page 26: бегун

Тестирование web-интерфейсаДоработки PHPUnit в части работы с SeleniumБолее подробный отчет об ошибкеДобавлены:URL страницы, скриншот, HTML-source, история команд

Selenium, лог запросов БД, отчет об ошибке в формате HTML

Переписан драйвер работы с Selenium-RCИсправление HTTP (Ускорение > 10 раз)Перекодировка запросов и ответовОтмена автоматического закрытия окна браузера при ошибках

Page 27: бегун

Тестирование web-интерфейсаОбщий подход: Testlib_TestCase_Selenium, ускорение авторизацииИспользовать Selenium как можно меньше!

Удаленный перехват методов и функций PHPОболочка MethodMock для удаленного перехвата.

Принцип работы:sessionId, Zend_Cache

Page 28: бегун

Тестирование JavaScriptTestlib_TestCase_JsBlock

Подмена в браузеренастраиваемый прокси для подмены ответов демонов и статических

файловвеб-нитерфейс для управлениями правилами

Page 29: бегун

Виртуальные стендыОднопоточность тестов• Переменные состояния программ• Общие объекты базы данных (один тест меняет конфигурацию других)• Общие динамические файлы на файловой системе• Lock wait-timeout базы• TcpdumpCatcher• lock- и log-файлы• DbiCleaner• FakeDaemon (чужие ответы)

Замедление тестов из-за сети, сложность поддержки распределенной с-мы.

Page 30: бегун

Виртуальные стендыДля каждого потока тестов должны быть свои:• тестируемая программа• база данных• динамические каталоги• сетевые порты/хосты для хождения к другим программам по

сети• каталоги с log- и lock-файлами

Желательно, чтобы все это было на одной машинеПереносим все в тестовый стенд!

Page 31: бегун

Виртуальные стендыВиртуальный компьютер на основе xenСоздание нового стенда:php /begun/www/teststand/createLab.php --daemon=<daemon>

[--start] [--swap]Для каждой программы своя конфигурация (по <daemon>):• Базы данных и таблицы (с данными и без)• Скрипт для установки тестируемой программы и зависимостей• Скрипт для запуска тестируемой программы и зависимостейПри создании стенда также делается checkout тестов, тестовых

библиотек и утилит

Page 32: бегун

Виртуальные стендыСписок виртуальных машин:xm list

Остановка/восстановление:xm start/destroy

Удаление:php /begun/www/teststand/deleteLab.php

Важно: база данных должна содержать только минимальный набор данных, необходимых для работы программы и тестов.

Page 33: бегун

Непрерывная интеграцияphpUnderControl. Стадии билда:• Создаем новый стенд• Компилируем тестируемую программу (C++)• Устанавливаем скомпилированную программу на стенд (C++)• Обновляем/выкачиваем код программы (PHP)• Обновляем/выкачиваем тесты и тестовые библиотеки• Прогоняем тесты• Удаляем тестовый стендБилды запускаются автоматически (интервал

времени/модификация кода)Отчет формируется автоматически

Page 34: бегун

Непрерывная интеграцияЧто запускать непрерывно:• тесты стабильной ветки, которые не прогоняются перед

выкладкой• тесты которые запускаются редко (текущая ветка и т.п.)• все «маленькое», что очень лень запускать вручную

Бонусы:• степень покрытия кода тестами• возможность автоматической генерации документации• выявление проблем кода (стандарты кодирования, кол-во

методов в классе, кол-во строк в методе, сложность кода, …)

Page 35: бегун

Конец первой части

Page 36: бегун

Часть 2

Нагрузочное тестирование

Page 37: бегун

Нагрузочное тестированиеМинусы тестовой среды: - Проблематичность создания адекватной нагрузки - Трудности в повторении среды production (железо, сеть, …) - Необходимость в интегральном подходе (связанные демона и

базы) - Необходимость в системах мониторинга - Необходимость в специалистах по нагрузочному тестированию

После функционального тестирования задача передается в нагрузочное тестирование.

Page 38: бегун

Нагрузочное тестированиеИнструмент для дублирования трафика• Безопасно дублирует production запросы (%)• Динамическое включение, выключение, перенацеливание• Гибкое управление долей дублированного трафика (от 0% до

N*100%)• Настраивается через web-интерфейс

Page 39: бегун

Нагрузочное тестированиеТестирование новых версийВыведение машины из productionУстановка на машину новой версииДублирование трафика с production-машины (%)Мониторинг нагрузки на подсистемы самой машины (диск, CPU,

сеть) и времени ответаМониторинг логов тестируемого демонаОценивается динамика нагрузки на «нижние» подсистемы

Page 40: бегун

Нагрузочное тестированиеСтресс-тестирование старых версийПостепенное увеличение нагрузки (>100%)Мониторинг кол-ва ошибочных ответов и времени ответаМониторинг нагрузки на подсистемы самой машины (диск, CPU,

сеть) и времени ответаМониторинг логов тестируемого демонаМониторинг нагрузки на failover-машинуИзучение логов балансера (время ответа)

Комплексная оценка производительности

Page 41: бегун

Конец второй части

Page 42: бегун

Часть 3

Эпилог

Page 43: бегун

Тестирование улучшает климат• код на production точно совпадает с кодом в стабильной ветке• утверждены стандарты кодирования (PHP)• новый/измененный код покрывается юнит-тестами (PHP)• введена процедура code-review• перед коммитом в стабильную ветку прогоняются тесты• подробные описания проектов в wiki• подробные описания задач в тикетах• введены обязательные декомпозиции разработки и

тестирования с оценкой трудозатрат• месячные планы

Page 44: бегун

Результаты и будущее Подробная документацияВнедрение проверенного кодаСтабильная и предсказуемая разработкаВыполнение месячных планов

Сбор и анализ формализованных отчетов об ошибке

Page 45: бегун

Ссылки + литератураPHP: http://php.net/PHPUnit: http://phpunit.de/ phpUnderControl: http://phpundercontrol.org/Selenium: http://seleniumhq.org/runkit: http://pecl.php.net/package/runkit

Extending and Embedding PHP by Sara Golemon

Мессарош, Дж. Шаблоны тестирования xUnit: рефакторинг кода тестов(XUnit Test Patterns: Refactoring Test Code)

Дастин, Э. Автоматизированное тестирование программного обеспечения: пер. с англ. / Э. Дастин, Дж. Рэшка, Дж. Пол

Page 46: бегун

СПАСИБО!

Дмитрий Зенович,руководитель отдела тестирования

E-mail: [email protected]