українська версія сайтуenglish website versionрусская версия сайта Deutsche Website Version
    ДомашняяОбучениеПосещение курсовПолучение работы QAСтатьиО насКонтактыНовостиФорум
     


    Мастерство в Обеспечении Качества ПО
    Подписаться письмом
    Наиболее популярные курсы Центра
    Какие курсы Центра Вам наиболее интересны?

     
      тел.: +38(044)45-900-46
      e-mail: classes@tester-training.com.ua

    Как тестировать AJAX приложения, часть 1
    Как правило, под тестированием AJAX приложений понимают обширный список тестов интерфейса и, в идеале, какое-то покрытие юнит-тестами. В этой статье мы перечислим основные проблемы такого подхода и, на примере простого приложения, использующего GWT, продемонстрируем эффективную стратегию тестирования, которая выходит за пределы тестирования интерфейса.

    Недостатки тестирования интерфейса

    В целом, тестирование интерфейса:

    • ресурсоемкое – требует много времени на написание и выполнение тестов
    • дает ограниченное представление о работе системы
    • часто ограничивается только «правильными маршрутами» (happy paths)
    • проверяет разные факторы в одном тесте
    • зависит от внешних факторов
    • требует дополнительной работы по настройке системы
    • возникающие проблемы сложно отслеживать и воспроизводить

    Хотя юнит-тесты не страдают от большинства из этих проблем, их нельзя оставить как единственный способ тестирования, поскольку они:

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

    Хотя данная проблема и не имеет решения, которое удовлетворило бы всех, есть ряд принципов, которые делают тестирование веб-приложений более эффективным:

    • разработка совместных тестов для модулей (требуется определить наименьшую подсистему)
    • распределение задач (подготовка к тесту не должна производиться через тестируемый интерфейс)
    • независимое тестирование интерфейсов (с заменой «заглушками» всего, кроме тестируемых модулей)
    • определение и использование зависимостей между модулями
    • использование разных стратегий и инструментов
    • нет, отказаться от тестирования интерфейса не получится

    Подход к тестированию

    Руководствуясь вышеперечисленными принципами, мы предлагаем следующий подход к тестированию веб-приложений:

    1. Изучите функциональность системы.
    2. Определите архитектуру системы.
    3. Определите способы взаимодействия компонентов системы.
    4. Определите взаимосвязи и условия, приводящие к ошибкам.
    5. Для каждой функции:
      • Определите используемые компоненты.
      • Определите потенциальные проблемы.
      • Создайте независимые тесты для каждой из проблем.
      • Создайте тест для «правильного маршрута».

    Примечание: ценность теста

    Разработчики часто спрашивают при написании тестов: «а стоит ли тратить на это время?» Краткий ответ на этот вопрос: «всегда!» Поскольку исправление дефекта намного дороже, чем его предотвращение, создание хороших тестов всегда стоит потраченного на это времени.

    Существует множество способов классифицировать тесты, наиболее распространенный из них основан на размере и области применения теста – каждый тест отвечает на свои вопросы о качестве продукта:

    • Юнит-тест: выполняет ли функция свое назначение?
    • Простой тест совместимости: могут ли два класса взаимодействовать друг с другом?
    • Тест совместимости: работает ли класс корректно со всеми связанными классами? Корректно ли он обрабатывает ошибки? Все ли необходимые функции могут быть вызваны через графический интерфейс?
    • Тест подсистемы: корректно ли две подсистемы взаимодействуют друг с другом? Способна ли каждая из них обрабатывать ситуация, когда другая подсистема работает неправильно?
    • Тест системы: работает ли корректно вся система в целом?

    Помните, что полезными являются те тесты, которые предоставляют быстрые и полезные результаты, то есть быстро определяют проблему и четко идентифицируют место ее появления.

     

    Перейдем к применению предложенного подхода на практике. В качестве примера мы рассмотрим простую систему управления ресурсами, позволяющую изменять количество имеющихся ресурсов в различных офисах. Это приложение использует GWT (Google Web Toolkit), но предлагаемый подход может быть применен к любому AJAX-приложению.

    Пройдем последовательно по всем шагам.

    1. Изучите функциональность системы.

    Как бы просто это ни звучало, это ключевой шаг в тестировании приложения. Вам нужно понять, как функционирует система – с точки зрения пользователя – прежде чем вы сможете начать писать тесты. Откройте приложение, осмотритесь, понажимайте на кнопки и ссылки, и просто «почувствуйте» приложение. Вот как выглядит приложение в нашем примере:

    навигационная панель приложения 

    Приложение имеет навигационную панель, позволяющую отображать ресурсы по названию офиса, просматривать список ресурсов в каждом офисе, уменьшать/увеличивать количество ресурсов, и сортировать список по офису и по ресурсу.

    2. Определите архитектуру системы.

    Изучение архитектуры системы – это следующий важный шаг. На этом этапе думайте о системе как о множестве компонентов, и представьте себе, как они общаются друг с другом. Изучение документации и архитектурных диаграмм полезно на этом шаге. В нашем примере мы имеем следующие компоненты:

    • GWT-клиент: код Java, скомпилированный в JavaScript, исполняемый браузером пользователя; связывается с сервером через RPC по HTTP
    • Сервлет: стандартный сервлет Apache Tomcat, который обслуживает «frontend.html» (главную страницу) с внедрённым JavaScript, а также обслуживает вызовы RPC для связи с клиентской частью JavaScript
    • Серверный компонент RPC: взаимодействует с сервлетом (с помощью вызовов RPC через HTTP) и с backend-компонентом (путем передачи буферов протокола (protocol buffers) через RPC)
    • Backend-компонент: занимается бизнес-логикой и работой с данными
    • База данных: отвечает за хранение данных, использует модель Bigtable

    Это поможет нарисовать простую диаграмму, представляющую потоки данных между этими компонентами (если такой схемы все ещё не существует):

    диаграмма потоков данных 

    3. Определите интерфейсы между компонентами.

    Некоторые очевидные интерфейсы:

    • Модуль gwt_module в файле сборки Ant
    • Сервлет Apache Tomcat
    • Реализация интерфейса RPC
    • Буферы протокола
    • База данных
    • Пользовательский интерфейс

    4. Определите взаимосвязи и условия, приводящие к ошибкам.

    Теперь нужно определить зависимости между интерфесками, и значения, которые можно использовать для проверки обработки ошибочных ситуаций.

    В нашем случае, пользовательский интерфейс обращается к сервлету, который обращается к StoreService (серверный компонент). Нам нужно проверить,что происходит, если StoreService:

    • возвращает NULL
    • возвращает пустой список
    • возвращает очень большой список
    • возвращает список с некорректным содержимым (неправильная кодировка, NULL-ы или пустые строки)
    • получает два параллельных запроса

    Кроме того, StoreService (серверный компонент) обращается к OfficeAdministration (backend-компонент). Опять нам нужно проверить обработку ошибочных ситуаций – что происходит, когда backend-компонент:

    • возвращает некорректные данные
    • не отвечает в течение ожидаемого времени
    • посылает два параллельных запроса
    • вызывает обработчик исключений

    Чтобы выполнить эти проверки, мы заменим StoreService модулем-«заглушкой», поведение которого мы можем контролировать, и проанализируем взаимодействие сервлета и заглушки. Аналогично, мы заменим OfficeAdministration на более простой модуль-«заглушку», и заставим StoreService взаимодействовать с ним.

     

    Для получения более четкого представления о взаимодействии компонентов, рассмотрим отдельные пользовательские сценарии. В качестве примера возьмем функцию фильтрации в пользовательском интерфейсе (в списке должны отображаться только ресурсы, относящиеся к офисам, помеченным галочкой).

    Анализ фильтра навигационной панели

    Клиент (пользовательский интерфейс):

    • получает список офисов через RPC
    • при помечании офиса, запрашивает список его ресурсов через RPC, и обновляет таблицу при получении списка
    • при снятии пометки с офиса, убирает его ресурсы из таблицы

    Серверный компонент:

    • получает список офисов от backend-компонента
    • получает список ресурсов для каждого офиса от backend-компонента

    Backend-компонент:

    • делает выборку офисов из базы данных
    • делает выборку ресурсов для заданного офиса из базы данных

    Наш следующий шаг – определить «минимальный тест», который позволит нам проверить, что все компоненты работают правильно.

    Проверка функциональности клиента

    Давайте проверим, что снятие пометки с офиса прячет его список ресурсов. Для этого нам небходимо знать, какие строки есть в таблице. Серверный компонент-«заглушка» может ограничиваться только этой задачей – и выдавать отдельный список, независимый от других тестов, работающих с теми же данными.

    Наша задача – сделать так, чтобы сервлет использовал MockStoreService – наш компонент-заглушку – в качестве серверного компонента. Этого можно добиться несколькими способами:

    • ввести флаг для переключения между компонентами
    • использовать шаблоны
    • переключать компоненты во время выполнения
    • добавить дополнительный конструктор в сервлет
    • создать отдельные настройки сборки приложения, которые используют компонент-заглушку
    • использовать внедрение зависимости (dependency injection) для замены полнофункционального компонента заглушкой

    Каждый из этих способов будет работать, выбор зависит от приложения. Решение добавить новый конструктор в сервлет приведет к зависимости рабочего кода от тестового, что, очевидно, не есть хорошо. Переключение между компонентами во время выполнения (путем манипуляций с загрузкой классов) будет работать, но может привести к дополнительным уязвимостям в системе защиты. Внедрение зависимости является гибким и удобным способом получить желаемый результат без вмешательства в рабочий код.

    Для реализации внедрения зависимости существуют различные модули; мы хотели бы предложить один из них, GuiceBerry – это модуль, который позволяет использовать структурную модель для компонентов приложения. Другими словами, если ваш тест зависит от определенных компонентов, вы можете «внедрить» их в приложение с помощью популярного инструмента Guice.

    В нашем примере, нам необходимо пометить объект StoreService ключевым словом «@Inject» в описании класса сервлета, и создать дополнительную имплементацию – MockStoreService – чтобы внедрить ее во время выполнения. Это можно сделать следующим образом:

    @GuiceBerryEnv(StoreGuiceBerryEnvs.NORMAL)

    public class StorePortalTest extends TestCase {

    @Inject

    StoreServiceImpl storeService;
    public void testStorePortal() {
    ...
    storeService.doSomething();
    ...
    }
    }

    Обратите внимание на строки, выделенные курсивом – это дополнения для GuiceBerry, позволяющие нам внедрить объект StoreServiceImpl в класс StorePortalTest. Определение StoreServiceImpl происходит в специальном классе GuiceBerry, называющемся NormalStoreGuiceBerryEnvs (и связаном с StorePortal через класс StoreGuiceBerryEnvs). Чтобы внедрить серверный компонент-«заглушку» в StorePortalTest, нам нужно создать MockStoreGuiceBerryEnvs (который породит «заглушку» для StoreService) и подменить им NormalStoreGuiceBerryEnvs во время выполнения. Все, что нам останется сделать – это указать следующие параметры JVM для теста:

    JVM_ARGS="-DNormalStoreGuiceBerryEnvs=MockStoreGuiceBerryEnvs"

    Этот пример демонстрирует только небольшую часть возможностей GuiceBerry; подробнее о нем можно узнать на официальном сайте.

     

    Этих изменений будет достаточно, чтобы отделить компонент-клиент от остальных –всю остальную работу сделает GwtTestCase; подробнее об этом можно почитать в этой статье.

    Не забудьте внедрить обработку остальных ошибочных сценариев через MockStoreService.

    Продолжение следует…

     

    (по материалам Google Testing Blog)
     
     вверх
    Копирайт © 2006 - 2007 CEQA.