Тестируй веб-сайты вместе с Badboy. Часть 3

June 2, 2008

Это последняя статья, которая рассказывает о badboy. Сегодня мы завершим рассмотрение методик тестирования сайта под “нагрузкой”, рассмотрим несколько “продвинутых” методик создания сценария тестирования.

В прошлый раз я остановился на том, что рассказывал об инструментах badboy (элементах сценария). Сегодня я продолжу рассказ о них, но основное внимание будет посвящено нагрузочному тестированию. Тестировать под нагрузкой сложно, это не сводится к банальному “запусти тысячу клиентов и посмотри когда сервер свалится от нагрузки”. Очевидно, что между двумя крайностями (сайт работает и не работает) есть множество промежуточных состояний (я не имею ввиду ситуации вида не работает одна страница из десяти – такой сайт не работоспособен). Фактор времени отклика (генерации страницы + отправка ее клиенту + отрисовка на экране браузера) не просто поддается регламентации: нужно учитывать человеческий фактор (с распространением широкополосного доступа массовый клиент все меньше и меньше желает ждать загрузки страницы), также учитывать перспективы роста целевой аудитории сайта (а… наш проект не масштабируется … это конец!). Очевидно, что оперировать средним временем отклика сайта столь же глупо как средней температурой по больнице. Для каждой страницы (для каждого сервиса, который предоставляет наш сайт) нужно четко сформулировать какова нижняя граница времени отклика (за которой сайт фактически не работоспособен). Также глупо оперировать цифрами вида количество посещений сайта 10000 в сутки. Необходимо учитывать распределение этих цифр по времени суток, нужно рассчитывать распределение и по популярности сервисов. Выделяют следующие виды тестирования: оценка производительности (здесь мы даем на вход системе некоторую нагрузку и смотрим не вышло ли время отклика за пределы допустимого). Фокус в том, что разовый съем показаний малополезен: по мере работы софта накапливаются ошибки, утечки ресурсов, так что никак не обойтись без нагрузочного тестирования, когда нагрузка подается в течение круглых суток и даже не одних. По мере эксплуатации системы происходит накопление данных (база данных сайта спустя пару лет вовсе не такая маленькая как первые месяцы – а сможет ли наш код работать с ней?), это тестирования объемов. Еще есть тестирование масштабируемости (а предположим, что мы добавили в систему памяти, дали новый процессор – какой это даст прирост производительности?). Еще выделяют стресс-тестирование (мне кажется, что не стоит отделять ее от оценки производительности, но раз “умные дядьки” так говорят …). В этом случае на вход системы подается просто невероятная нагрузка, и мы смотрим, какая часть системы отказала (по крайней мере, мы будем знать то ли закупать “железо”, то ли нанимать новых программистов). Еще можно заниматься тренировкой навыка персонала устранять сбои: например, отказал один из серверов в кластере, что нужно сделать на время починки, чтобы пользователи, видя не рабочий сайт, не слишком обижались? Напоследок, не забывайте, что прочность цепи определяется ее самым слабым звеном.

Для точной имитации поведения пользователя необходимо вводить задержки. Действительно, не будет же человек, словно робот, постоянно жать на ссылки, переходя ко все новым и новым страницам, не при этом читая ответов. Итак, для того чтобы между двумя шагами (загрузками страниц) сделать паузу в badboy применяется элемент “Timer”. Пауза может быть постоянной (в миллисекундах) или случайной величиной в некотором отрезке. Если добавить Timer как первый элемент в тесте, а затем в его свойствах отметить галочку “Cascade delay to following items”, то пауза будет предварять все шаги в рамках теста.

При тестировании сайта под нагрузкой возникает вероятность timeout-ов, т.е. неполучения ответа от сервера в приемлемое время. Откройте свойства элемента сценария “URL Request” и посмотрите на закладку “Playing”. Если вы там включите галочку “Enable Timeout”, введете в текстовое поле значение этого timeout-а (в секундах), то затем можете указать, что должен делать badboy, когда время ожидания ответа превысило предельную величину. Как варианты: можно продолжить выполнять тест дальше, можно прервать весь тест или только текущий шаг, а можно повторить шаг, в который вложен объект запроса еще раз (здесь будьте осторожны).

Создание сложных сайтов интегрированных с различными подсистемами предприятия порождает проблему распределения сценария тестирования во времени (когда часть обработки данных не может быть выполнена мгновенно). Например, вы загружаете на сайт файл с avi-фильмом, затем он должен подвергнуться перекодированию в flash video формат. Очевидно, что в реальности эта операция не будет мгновенной и может занять десяток-другой минут, если не часов. Поэтому в badboy предусмотрели механизм выполнения заданий по расписанию. Точнее говоря, расписание бывает двух видов: глобальное и “для конкретной задачи”. В первом случае вы активируете меню “Tools -> Schedule Execution”, в появившемся диалоговом окне (вообще-то это не часть badboy, а встроенная в windows функция “планировщика заданий”, так что проверьте, чтобы его служба была запущена) указываются время выполнения текущего скрипта. Второй вариант планирования расписания (как часть сценария теста) создается с помощью инструмента “Schedule Item”. В свойствах элемента укажите скрипт, который должен быть запущен (другой файл badboy) и время исполнения (либо немедленно, либо отложить на заданное количество часов или минут).

Основной элемент позволяющий создать имитацию множества клиентов работающих с сайтом - это “Thread Control”. Внутрь его вы помещаете те самые Steps или непосредственно объекты http-запросов, assertions, timers и другие. Возможен и другой вариант (более удобный, если вы уже создали Step и наполнили его элементами сценария) – просто вызовите контекстное меню для Step и выберите пункт “Convert to Thread Item”. Что такое “Thread Control”? Воспринимайте его как точку сценария, в которой badboy создает некоторое количество собственных виртуальных копий. И каждая из этих копий начнет независимо от другой выполнять все шаги сценария вложенные внутрь “Thread Control”. Стало понятнее? Не совсем. Во-первых, что такое “виртуальная копия”? Мы ведь программисты и нам нужно четкое определение этого понятия. Что будет если я решу запустить одновременно 100 “виртуальных копий”? Сможет ли badboy обеспечить равномерную выдачу запросов к серверу, или “свалится” из-за нехватки ресурсов. Есть три вида подобных виртуальных копий badboy: Raw, External MSHTML, MSHTML. Если вы откроете окно настроек “Thread Control” (см. рис. 1),



то заметите падающий список “Browser Engine”, в котором и перечисляются эти значения. Самый простой вариант “Raw” – в этом режиме badboy выполняет отправку http-запросов к серверу, затем полученные данные проверяет на корректность с помощью assertion и check-ов (все как обычно). Но полученные с сервера данные не загружаются в окно браузера badboy, а, следовательно, не будут работать все элементы сценария, которые имитируют действия пользователя с интерфейсом страницы (клик мышью, имитация нажатия клавиатуры … - все эти команды создаются в режиме записи Navigation). Второй режим, равно как и третий, предполагают, что выполняться должны все записанные в сценарии шаги (полная поддержка режима Navigation). Для того чтобы имитировать работу с интерфейсом, badboy запускает некоторое количество копий браузера internet explorer (поменять его на opera или firefox не возможно). Главное отличие “External MSHTML” от просто MSHTML в том, что в первом случае новые копии internet explorer-а создаются отдельно друг от друга (каждый из них как отдельный процесс), а значит что помимо повышенных нагрузок на процессор и память, мы получаем дополнительный уровень защищенности. Вспомните, что происходит, когда мы открываем несколько закладок в internet explorer, затем в одной из них заходим на, например, сайт mail.ru? Во второй закладке если мы откроем ту же страницу mail.ru, то нас не будут спрашивать пароль, т.к. у всех экземпляров окон одного браузера общие ресурсы (cookie, в которых хранятся сведения об авторизации). Отсюда вывод, что использовать режим MSHTML, несмотря на его “легкость”, можно только в случае, если запросы, посылаемые каждым из потоков, не пересекаются. В практике все гораздо сложнее и если внимательно читать справку badboy, то можно найти дополнительные ограничения, которые накладываются на комбинацию сценарий-движок. Рекомендуется в настройках badboy включить запись в файл журнала выполняемых действий (меню “Preferences” закладка “Logging”), так вы можете найти ошибки, пропущенные шаги сценария. А лучше воспользуйтесь моим опытом: делайте два параллельных набора тестов: на корректность интерфейса и на производительность сайта, и никогда первый из них не запускайте в режиме “Thread Control”, а для второго не используйте режим записи “Navigation”. И еще техническая деталь: т.к. режим “External MSHTML” фактически означает запуск еще одной копии badboy, то не забудьте сохранить файл сценария. При установке количества потоков в графе “maximum number of thread” помните, что нагрузка идет не только на тестируемый сервер, но и на ваш компьютер (и уж тем более, вверх глупости - тестировать серьезное приложение, разместив на одной машине и сервер и badboy). Учите также, что если вы не создали имитацию “медленного” пользователя с помощью Timer-ов, то один виртуальный процесс значительно превосходит по генерируемой нагрузке человека. Определив количество процессов, настроим правила по которым они запускаются и останавливаются. Снова откройте свойства “Thread Control”. Видите, есть два режима: одновременно для всех или же запуск и остановка потоков будут выполняться постепенно, по очереди. В этом случае мы можем настроить, что, например, каждые 3 секунды стартуют очередная пятерка процессов. Последний параметр управляющий работой “Thread Control” - это “Duration” (время в течении которого выполняется скрипт). Самым простым режимом является “Run for Specified Iterations” (все шаги, вложенные в данный “Thread Control ”, будут выполнены фиксированное количество раз). Есть и более “хитрые” режимы: “run for fixed time” и “Stop when step exits”. В первом случае время работы потока ограничено не количеством шагов, которые нужно сделать, а, именно, общим временем выполнения (этот режим вовсе не означает, что если время выполнения какого-либо из запросов превзошло лимит времени, то он будет прерван). Если какой-то из процессов завершил выполнение сценария, а время работы еще не было исчерпано, то процесс запускается повторно. Режим “Stop when step exits” означает, что как только будет завершено выполнение теста, в который вложен “Thread Control”, то и выполнение потоков будет завершено. Здесь нужно сделать комментарий: запуск “thread control” выполняется как бы “мгновенно”, а управление сценарием переходит на следующий элемент. Завершая рассказ о нагрузочном тестировании, осталось только показать способ выполнить настройки каждого из процессов индивидуально. В практике тестирования ситуация, когда идентичный сценарий выполняется одновременно группой клиентов – это что-то из разряда абсурда. Вернувшись к старому примеру с формой регистрации на почтовом сайте, как вы думаете, что будет, если каждый из процессов будет пытаться зарегистрироваться под одним и тем же именем? Естественно, что сценарий будет не работоспособен. Нам нужен способ “привязать” к каждому из процессов собственный набор переменных. В прошлой статье я показал то, как работать с “Data Source”, как создать переменную, значения которой берутся из таблички excel. Сделаем еще один шаг: я беру старый файл excel с двумя колонками (fio, age), импортирую его, привязываю к переменным. Теперь создаю “Thread Control”, настраиваю свойства потоков и в качестве единственного действия указываю загрузку сприпта php с переменными как показано на рис. 2



(переменная ${threadNum} – служебная переменная badboy указывающая номер для каждого из процессов). Последний шаг – анализ результатов тестирования. В простейшем случае вы можете запустить тест, а затем переключиться на закладку “Graph” и наблюдать за меняющимися графиками. Если работа теста занимает много времени или сценарий запускается по расписанию, то можно создать такой сценарий: Test -> Thread Control -> Запросы. Затем на уровень Test добавить Timer, величина задержки которого достаточна для того, чтобы завершить все тесты в рамках группы потоков, а после Timer-а поместить элемент Screenshot. Который умеет делать не только копии экрана браузера, но и графика производительности (здесь вам нужно будет купить лицензию, но разве на спичках экономят?).

Когда создается действительно большой сценарий тестирования, то бывает тяжело ограничиться одним лишь представлением в виде дерева Suite->Test->Step. Гораздо лучше воспользоваться такой функцией badboy, как “Aggregate Script”. Затем вы указываете файл, который должен быть вызван из основного сценария и то будут ли в этот сценарий переданы переменные из главного сценария (галочка “Inherit variables”). Момент в том, что, если вызванный скрипт сам содержит переменные с такими же именами как родительский, то переменные будут замещены. Помните, что изменение переменных в дочернем скрипте никак не отображается на родительский.

Теперь я расскажу об одной из наиболее важных возможностей badboy – создание сложных сценариев взаимодействующих с самим badboy. Если возможностей стандартных для badboy элементов сценариев вам не хватает, то можно создать и собственный элемент. В прошлой статье, когда я рассказывал о видах Check-ов (условий), то упомянул о “Jscript Check”. Когда создаете такой check, то вы должны указать в его свойствах произвольный (да-да, произвольный) фрагмент javascript, который может проверять любые условия на странице (с условными операторами, циклами, обращениями к дереву DOM вебстраницы – все что угодно). Единственное требование: скрипт должен вернуть (оператором return) одно из двух значений: true|false (тест пройден или тест провален).
  1. // использую Jquery для проверки наличия на странице тега H1 с классом macros
  2. return $('h1.macros').length > 0;


Общая рекомендация: при тестировании сложных страниц, подгружающих внешние ресурсы или что-то вычисляющих на стороне клиента, всегда давайте badboy на это время. Например, в примере выше для работы check-а мне требуется библиотека jquery, а ее загрузка не мгновенная. Так что в свойствах “URL Request” вы можете поставить опцию паузы в полсекунды (закладка “Playing”), либо отметить “галочку” напротив опции “wait explicitly for child requests to complete loading ”.

Кроме построенного на базе javascript check-а, в составе badboy есть и более гибкий механизм “Jscript” (закладка “tools”). Поместив такой элемент в состав сценария, вы получаете возможность написать на javascript некоторый код, который будет выполнен браузером (это значит, что, равно как и javascipt check, этот прием работает не во всех режимах “проигрывания” сценария). Вы можете также указать то, в каком из фреймов будет проигран код. А если нажмете в окне редактора (скажем, прямо это далеко не dreamweaver) комбинацию ctrl+пробел, то badboy подскажет вам стандартных функциях javascript и (вот, оно!) специальном объекте badboy, добавленном для того, чтобы наш скрипт был не ограничен только возможностями браузера, но мог попросить какую-нибудь “услугу” у самого badboy. Далее я опишу основные команды badboy:

badboy.getVariableNames () Возвращает строку с именами всех badboy переменных (через запятую).

badboy.getVariable ('fio') А так можно получить значение некоторой переменной.

badboy.setVariable ('fio', 'mark') А так мы изменяем значение переменной.

badboy.sleep (2000) Пауза на две секунды.

badboy.eval('${fio}') А так мы можем вычислить некоторое выражение.

badboy.addValue ('fio', ‘next user') Если переменная fio привязана к списку значений, то в конец этого списка будет помещено величина ‘next user’.

badboy.save ('Report', 'c:\\file.html', 'exportImages=true') Так мы сохраним в файл отчет о выполнении теста.

badboy.save ('BrowserContent', 'c:\\file.html', 'exportImages=true') А так будет сохранено в файл содержимое текущего окна браузера.

badboy.clearResponses() Если вызов такой команды поместить как первый шаг сценария, то будет автоматически удалена вся история http-запросов с прошлого запуска.

На этом я буду считать, что рассказ о badboy завершен. Как вывод: помните, что тестирование – важный этап разработки любого ПО. Помните, что чем раньше вы внедрите его в свой процесс работы, тем меньше будет переделок, авралов и потерянного здоровья. Помните, что badboy не единственный программный продукт этого класса, что тестирование может выполнять более детально на шагах: работа с базой данных, логика, формирование html-кода, пересылка данных, действия на стороне клиента. Помните, что тестирование нужно выполнять, держа в уме тот факт, что настоящий веб-сайт посещается не одним человеком, и лишь по мере роста нагрузки могут проявиться “плавающие ошибки”. Таким образом, тестирование сайта под нагрузкой является обязательным требованием.

Сегодня мне опять никак не удалось перейти к рассказу о Jmeter, но может это и к лучшему: я попробую сделать отдельную статью посвященную ему.