бегун

924 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
924
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
6
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

бегун

  1. 1. Тестирование в компании Дмитрий Зенович
  2. 2. Начальные условия Десятки типов онлайн демонов на C++ Десятки offline скриптов на PHP и демонов на C++ веб-интерфейс API генераторы статистики и отчетов Объем кода – несколько сотен тысяч строк
  3. 3. Начальные условия Что хотелось сделать: • сократить время итерации и увеличить продуктивность разработки • создать отдел тестирования (тестирование проводилось силами разработчиков) • увеличить полноту документации • оптимизировать процесс внедрения в PHP -разработке
  4. 4. Часть 1 Тестирование
  5. 5. Начало тестирования Ставка на автоматизированное тестирование . Выбор в качестве фреймворка для автоматизированного тестирования PHPUnit Стандарты кодирования . Один класс в файле. Мэппинг имени класса в имя файла. Непрерывный рефакторинг. Переписывать все непонятное, упрощать все сложное, избавляться от дублирования.
  6. 6. Библиотека / ConfigurableObject <ul><li>Testlib_ConfigurableObject </li></ul><ul><li>Эмуляция массива (доступ по индексу для свойств) </li></ul><ul><li>Свойства типизированы </li></ul><ul><ul><ul><li>/** </li></ul></ul></ul><ul><ul><ul><li>* HTTP-method </li></ul></ul></ul><ul><ul><ul><li>* @var string </li></ul></ul></ul><ul><ul><ul><li>*/ </li></ul></ul></ul><ul><ul><ul><li>public $method = 'GET' ; </li></ul></ul></ul><ul><li>Конструктор принимает на вход массив со значениями свойств </li></ul>
  7. 7. Библиотека / ConfigurableObject Защищенные свойства /** * @property * @var string */ protected $readOnly ; protected function _setReadOnly ( $value ) { throw … } protected function _getReadOnly () { return 1 ; }
  8. 8. Библиотека / ConfigurableObject Сериализация : toArray() , __toString() Сериализация в XML @serializeXmlAs node(имя)|attribute|text|cdata|none @serializeXmlAs node( имя ) в заголовке класса $obj = Testlib_ConfigurableObject :: fromXml ( 'XmlObj' , $xmlString ) ; $obj-> to Xml ( ) ; Другие виды сериализации Внутренние форматы сериализации, JSON, HTML , …
  9. 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 ) ;
  10. 10. Библиотека / MethodMock Testlib_MethodMock Изоляция тестируемого функционала Предотвращение лишних действий Предотвращение долгих операций в тестах (БД и т.п.) Упрощение подготовки начальной конфигурации PECL- модуль runkit (runkit_method_copy, runkit_method_redefine, runkit_method_remove, …) Перехват встроенных функций PHP Патчи runkit
  11. 11. Библиотека / MethodMock $ m 1 = Testlib_MethodMock :: interceptMethodByCode ( &quot;MyClass&quot; , &quot;myMethod&quot; , &quot;echo ' 1 ';&quot; ) ; $ m2 = Testlib_MethodMock :: mockFunctionResult ( 'getValue' , 12345 , array ( 2 , 1 )) ; $ m3 = Testlib_MethodMock :: interceptMethodByCode ( &quot;MyClass&quot; , &quot;myMethod&quot; , &quot;echo 'called';&quot; ) ; $ m1 -> getCalledArgs () ; $ m2 -> getCalledResults () ; $ m3 -> isCalled () ; $ m1 -> countCalled () ; $m 2 -> resetCallStack () ; $m 3 -> revert () ;
  12. 12. Библиотека / TcpdumpCatcher Testlib_TcpdumpCatcher $catcher = new Testlib_TcpdumpCatcher ( TESTLAB_HOST , TESTLAB_USER , TESTLAB_PASS , &quot;192.168.1.3&quot; , &quot;8080&quot; , &quot;eth0&quot; ) ; // ... $dump = $catcher -> finish () ; Внутри: tcpdump , анализ собранных пакетов, склеивание с учетом фрагментирования, парсинг Работает локально или удаленно
  13. 13. Библиотека / LogCatcher Testlib_LogCatcher $catcher = new Testlib_LogCatcher ( &quot;my.log&quot; , /* &quot;192.168.1.3&quot; , &quot;myuser&quot; , &quot;123456“ */ ) ; // ... $log = $catcher -> finish () ; Внутри: wc -l , tail -n Работает локально или удаленно
  14. 14. Работа с базой данных <ul><li>MySQL, транзакционность InnoDB, реализация вложенных транзакций </li></ul><ul><li>Testlib _ TestDb </li></ul><ul><li>Zend_Db </li></ul><ul><li>реализация вложенных транзакций </li></ul><ul><li>фабрика на основе конфигураций </li></ul><ul><li>остановка/восстановление репликации </li></ul><ul><li>Очистка по расписанию </li></ul>
  15. 15. Библиотека / DatabaseMock Testlib_DatabaseMock $dbMock = new Testlib_DatabaseMock ( 'Cards' , 'CardsForTest' , false , array ( 'CatalogTree' , 'CatalogInfo' )) ; // ... $dbMock -> revert () ; Внутри : Testlib_MethodMock::interceptFunction('mysql_query'); runkit_constant_redefine (…) Различные способы экранирования имен таблиц и баз учитываются
  16. 16. Библиотека / DbiCleaner Testlib_D biCleaner $dbMock = new Testlib_DatabaseMock ( 'Cards' , 'CardsForTest' , false , array ( 'CatalogTree' , 'CatalogInfo' )) ; // ... $dbMock -> revert () ; Внутри : MethodMock::interceptFunction('mysql_query'); runkit_constant_redefine (…) Различные способы экранирования имен таблиц и баз учитываются
  17. 17. Библиотека / FakeDaemon Testlib_ FakeDaemon Подмена ответов. nginx + php. Настройка ini- файла: [:7099] serviceNam e=&quot;4test&quot; port =80 host =&quot;lab128&quot; logfile =&quot;./7099-4tests.log &quot; Подмена ответа: Testlib_FakeDaemon :: setCustomResponse ( '4test' , $requestText , $responseText ) ;
  18. 18. Библиотека / FakeDaemon $ responseText может не содержать HTTP -заголовков $requestText может быть неполным текстом запроса Внутри подмены : Testlib_FakeDeamon_CustomResponse::fromRequestAndResponse() Создаются условия по requestMethod , handler , каждому get -параметру , по каждому заголовку , кроме « Host ». Порядок полей не важен. Ответ и его условия сохраняются в базу и заносятся в DbiCleaner
  19. 19. Библиотека / FakeDaemon <ul><li>Обработка запроса </li></ul><ul><li>Определение конфигурации по порту </li></ul><ul><li>Получение всех подмененных запросов для сервиса (в обратном порядке) </li></ul><ul><li>Для каждой подмены проверяются условия ( И , urldecode для get-параметров) </li></ul><ul><li>Если подмена найдена, то возвращается она, иначе ответ ищется в кэш , если там нет, то берется ответ настоящего демона. </li></ul><ul><li>Позволяет тестировать программы независимо! </li></ul><ul><li>Тест должен очищать после себя подмены. </li></ul>
  20. 20. Создание клиента daemon’ а Базовый класс ( Testlib_ClientTest ) Работа с конфигом демона в БД (получение, изменение) Абстрактный http- запрос к демону Абстрактный http-запрос к демону для тестов ( конфиг, лог, подмена ответов других демонов, TcpdumpCatcher , контроль изоляции демона, поиск ошибок в логе) Запуск и остановка демона , запущен ли демон, ожидание запуска requestHandler , requestHandlerForTests ( Testlib_ConfigurableObject )
  21. 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 ) для входных и выходных данных хэндлеров
  22. 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 ) ;
  23. 23. Библиотека Базовые классы тестов и сьютов демонов Testlib _ TestSuite остановка и возобновление репликации prepare/clean up CommonSuite (много тестов для одного запроса к демону)
  24. 24. Тестирование PHP Можно использовать библиотеки PHP-разработчиков Методы белого ящика Написание юнит-тестов PHP Базовый кла c с Testlib_TestCase_Php Блокирование коммита БД C тарт транзакции в setUp, откат в tearDown Очистка глобальных объектов Список sql-запросов в отчетах об ошибке Отслеживание коммитов и роллбэков Проблема parent::setUp и parent::tearDown
  25. 25. Тестирование PHP Testlib_ScriptRunner Возвращать результат, вывод, log скрипта Умеет дожидаться освобождения lock -файла run S cript () runScriptByIncluding () удаляет константы, определенные в скрипте удаляет функции, определенные в скрипте неправильное окружение, зато есть возможность подменять методы, базы, откатывать транзакцию
  26. 26. Тестирование web- интерфейса Доработки PHPUnit в части работы с Selenium Более подробный отчет об ошибке Добавлены: URL страницы, скриншот , HTML- source , история команд Selenium , лог запросов БД , отчет об ошибке в формате HTML Переписан драйвер работы с Selenium-RC Исправление HTTP ( Ускорение > 10 раз) Перекодировка запросов и ответов Отмена автоматического закрытия окна браузера при ошибках
  27. 27. Тестирование web- интерфейса Общий подход: Testlib _ TestCase _ Selenium , ускорение авторизации Использовать Selenium как можно меньше!  Удаленный перехват методов и функций PHP Оболочка MethodMock для удаленного перехвата . Принцип работы: sessionId, Zend_Cache
  28. 28. Тестирование JavaScript Testlib_TestCase_J sBlock Подмена в браузере настраиваемый прокси для подмены ответов демонов и статических файлов веб-нитерфейс для управлениями правилами
  29. 29. Виртуальные стенды <ul><li>Однопоточность тестов </li></ul><ul><li>Переменные состояния программ </li></ul><ul><li>Общие объекты базы данных (один тест меняет конфигурацию других) </li></ul><ul><li>Общие динамические файлы на файловой системе </li></ul><ul><li>Lock wait-timeout базы </li></ul><ul><li>TcpdumpCatcher </li></ul><ul><li>lock- и log-файлы </li></ul><ul><li>D bi Cleaner </li></ul><ul><li>FakeDaemon (чужие ответы) </li></ul><ul><li>Замедление тестов из-за сети, сложность поддержки распределенной с-мы. </li></ul>
  30. 30. Виртуальные стенды <ul><li>Для каждого потока тестов должны быть свои: </li></ul><ul><li>тестируемая программа </li></ul><ul><li>база данных </li></ul><ul><li>динамические каталоги </li></ul><ul><li>сетевые порты/хосты для хождения к другим программам по сети </li></ul><ul><li>каталоги с log - и lock -файлами </li></ul><ul><li>Желательно, чтобы все это было на одной машине </li></ul><ul><li>Переносим все в тестовый стенд! </li></ul>
  31. 31. Виртуальные стенды <ul><li>Виртуальный компьютер на основе xen </li></ul><ul><li>Создание нового стенда : </li></ul><ul><li>php /begun/www/teststand/createLab.php --daemon=<daemon> [--start] [--swap] </li></ul><ul><li>Для каждой программы своя конфигурация (по <daemon>): </li></ul><ul><li>Базы данных и таблицы (с данными и без) </li></ul><ul><li>Скрипт для установки тестируемой программы и зависимостей </li></ul><ul><li>Скрипт для запуска тестируемой программы и зависимостей </li></ul><ul><li>При создании стенда также делается checkout тестов, тестовых библиотек и утилит </li></ul>
  32. 32. Виртуальные стенды Список виртуальных машин: xm list Остановка/восстановление: xm start/destroy Удаление: php / begun / www / teststand / deleteLab . php Важно: база данных должна содержать только минимальный набор данных, необходимых для работы программы и тестов.
  33. 33. Непрерывная интеграция <ul><li>phpUnderControl. Стадии билда: </li></ul><ul><li>Создаем новый стенд </li></ul><ul><li>Компилируем тестируемую программу (C++) </li></ul><ul><li>Устанавливаем скомпилированную программу на стенд ( C ++) </li></ul><ul><li>Обновляем/выкачиваем код программы (PHP) </li></ul><ul><li>Обновляем/выкачиваем тесты и тестовые библиотеки </li></ul><ul><li>Прогоняем тесты </li></ul><ul><li>Удаляем тестовый стенд </li></ul><ul><li>Билды запускаются автоматически (интервал времени/модификация кода) </li></ul><ul><li>Отчет формируется автоматически </li></ul>
  34. 34. Непрерывная интеграция <ul><li>Что запускать непрерывно: </li></ul><ul><li>тесты стабильной ветки, которые не прогоняются перед выкладкой </li></ul><ul><li>тесты которые запускаются редко (текущая ветка и т.п.) </li></ul><ul><li>все «маленькое», что очень лень запускать вручную </li></ul><ul><li>Бонусы: </li></ul><ul><li>степень покрытия кода тестами </li></ul><ul><li>возможность автоматической генерации документации </li></ul><ul><li>выявление проблем кода (стандарты кодирования, кол-во методов в классе, кол-во строк в методе, сложность кода, …) </li></ul>
  35. 35. Конец первой части
  36. 36. Часть 2 Нагрузочное тестирование
  37. 37. Нагрузочное тестирование Минусы тестовой среды: - Проблематичность создания адекватной нагрузки - Трудности в повторении среды production (железо, сеть, …) - Необходимость в интегральном подходе (связанные демона и базы) - Необходимость в системах мониторинга - Необходимость в специалистах по нагрузочному тестированию После функционального тестирования задача передается в нагрузочное тестирование.
  38. 38. Нагрузочное тестирование <ul><li>Инструмент для дублирования трафика </li></ul><ul><li>Безопасно дублирует production запросы (%) </li></ul><ul><li>Динамическое включение, выключение, перенацеливание </li></ul><ul><li>Гибкое управление долей дублированного трафика (от 0% до N*100%) </li></ul><ul><li>Настраивается через web -интерфейс </li></ul>
  39. 39. Нагрузочное тестирование Тестирование новых версий Выведение машины из production Установка на машину новой версии Дублирование трафика с production-машины (%) Мониторинг нагрузки на подсистемы самой машины (диск, CPU, сеть) и времени ответа Мониторинг логов тестируемого демона Оценивается динамика нагрузки на «нижние» подсистемы
  40. 40. Нагрузочное тестирование Стресс-тестирование старых версий Постепенное увеличение нагрузки (>100%) Мониторинг кол-ва ошибочных ответов и времени ответа Мониторинг нагрузки на подсистемы самой машины (диск, CPU, сеть) и времени ответа Мониторинг логов тестируемого демона Мониторинг нагрузки на failover-машину Изучение логов балансера (время ответа) Комплексная оценка производительности
  41. 41. Конец второй части
  42. 42. Часть 3 Эпилог
  43. 43. Тестирование улучшает климат <ul><li>код на production точно совпадает с кодом в стабильной ветке </li></ul><ul><li>утверждены стандарты кодирования (PHP) </li></ul><ul><li>новый/измененный код покрывается юнит-тестами ( PHP ) </li></ul><ul><li>введена процедура code-review </li></ul><ul><li>перед коммитом в стабильную ветку прогоняются тесты </li></ul><ul><li>подробные описания проектов в wiki </li></ul><ul><li>подробные описания задач в тикетах </li></ul><ul><li>введены обязательные декомпозиции разработки и тестирования с оценкой трудозатрат </li></ul><ul><li>месячные планы </li></ul>
  44. 44. Результаты и будущее Подробная документация Внедрение проверенного кода Стабильная и предсказуемая разработка Выполнение месячных планов Сбор и анализ формализованных отчетов об ошибке
  45. 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) Дастин, Э. Автоматизированное тестирование программного обеспечения: пер. с англ. / Э. Дастин, Дж. Рэшка, Дж. Пол
  46. 46. СПАСИБО! Дмитрий Зенович, руководитель отдела тестирования E-mail: dzenovich@ begun.ru

×