Статьи

Исполняемые спецификации: автоматизация ваших требований

Одной из самых больших проблем в разработке программного обеспечения является проблема «СДЕЛАНО». Мы имеем в своем распоряжении стопку учетных карточек, представляющих пользовательские истории, и нам поручено преобразовать их в работающее программное обеспечение. Как мы узнаем, когда мы достигли нашей цели?

То есть, учитывая отдельную историю, как мы узнаем, когда закончим с ней?

  • Когда это закодировано?
  • Кодировано и проверено?
  • Кодированы, проверены и развернуты?
  • Кодированы, протестированы, развернуты и проверены нашим клиентом?

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

В гибкой разработке мы разработали это понятие пользовательской истории. Рон Джеффрис разбивает концепцию пользовательской истории на три важные части:

  • Карта: это может быть учетная карточка, заметка, виртуальная карта в какой-либо системе отслеживания и т. Д. Она предназначена в качестве заполнителя, который напоминает нам о том, что история существует и что нам нужно что-то с ней сделать.
  • Разговор: На самом деле, главная цель карты — побудить нас к совместным переговорам между разработчиками и клиентами. Эти разговоры создают петлю обратной связи, которая позволяет истории (и результирующему коду) развиваться с потребностями бизнеса, поскольку они медленно кристаллизуются. В результате этих разговоров …
  • Подтверждение: Как мы узнаем, когда эта история является полной и правильной? Эти подтверждения обычно принимают форму одного или нескольких приемочных испытаний. Обычно это пошаговое описание поведения системы в ответ на взаимодействие с пользователем. Они очень похожи на сценарии, которые мы ранее определили как часть «вариантов использования», но с меньшей, более сфокусированной областью действия.

ТИПИЧНЫЙ КОНТРОЛЬ ПРИЕМКИ:

  1. Данный пользователь находится на экране каталога магазина.
  2. Когда пользователь нажимает на изображение товара.
  3. И пользователь нажимает «Добавить в корзину».
  4. Затем отображается экран корзины покупок с добавленным продуктом и обновлением общей цены.

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

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

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

Итак, что является основой аргумента для этого подхода? Давайте посмотрим на типичный тест:

@Test
public void testFindSpeakerFlow() throws Exception {
selenium.open("/fluffbox-rwx/");
selenium.click("//a[contains(@href, '/fluffbox-rwx/speaker/find')]");
selenium.waitForPageToLoad("30000");
selenium.click("link=Matt Stine");
selenium.waitForPageToLoad("30000");
selenium.click("link=Find this Speaker");
selenium.waitForPageToLoad("30000");
selenium.click("link=RENT NOW");
selenium.waitForPageToLoad("30000");
selenium.click("link=Continue");
selenium.waitForPageToLoad("30000");
selenium.type("username", "joeuser");
selenium.type("password", "password");
selenium.click("remember_me");
selenium.click("loginButton");
selenium.waitForPageToLoad("30000");
assertTrue(selenium.isTextPresent("This concludes your fake rental experience!"));
}

Здесь у нас есть тест, написанный для приложения Fluffbox (
http://github.com/mstine/fluffbox-rwx ). Он начинается с просмотра базового URL-адреса, нажатия на … «что-то», определяемое этим непрозрачным выражением XPath, ожидания 30000 «чего-то» и т. Д. В конце концов мы утверждаем, что «где-то» присутствует определенная фраза текста.

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

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

Ручные тесты в лучшем случае «записываются» в виде сценариев либо на бумаге, либо, возможно, в «системе управления планом тестирования». Бумажные сценарии по необходимости чрезвычайно сложно развивать с приложением. Такое жесткое разделение между кодом и тестами неизбежно приводит нас к ситуации, когда код и тесты не синхронизированы друг с другом. Как правило, результатом является гораздо более «специальный» способ тестирования приложения. Это непоследовательный и повторяемый способ. Это грязный путь. Способ, который позволяет ошибкам проникать в производство.

И вот мы попадаем в «CATCH-22». Нам нужно проверить. Если мы автоматизируем тесты, они слишком дороги в обслуживании. Если мы проводим тестирование вручную, мы тратим время на умственного работника, делая механический труд, и мы даже не справляемся с этим!

Но что если … мы могли бы написать исполняемые ориентированные на пользовательский интерфейс спецификации, которые не были бы хрупкими? Мы составим решение этой проблемы из трех независимых строительных блоков:

среда разработки, управляемой поведением (BDD)

BDD стремится расширить сотрудничество между заказчиками и разработчиками путем разработки автоматизированных тестов, называемых «спецификациями», с использованием сред, предоставляющих DSL для написания тестов на языке, очень близком к письменной прозе. Эти спецификации затем перемежаются с логикой, которая взаимодействует с тестируемым приложением (AUT). Наконец они подтверждают, что произошло ожидаемое поведение. Некоторые популярные примеры сред BDD включают
RSpec ,
Cucumber ,
easyb и
Spock .

Шаблон объекта Page Шаблон объекта

Page — это недавнее повторное применение шаблона Facade для тестирования веб-приложений. Его цель — отделить две очень ортогональные проблемы друг от друга:

  • Модель логического взаимодействия приложения. Здесь мы описываем тот факт, что на странице приложения есть определенные поля формы, которые мы можем заполнить, кнопки и ссылки, которые мы можем щелкнуть, и текст, который мы можем прочитать.
  • Базовая структура документа HTML. Здесь мы ссылаемся на используемые теги HTML, как они вложены друг в друга, какие стили CSS применяются, какие типы виджетов форм используются и как они реализованы и т. Д. Короче говоря, что меняется достаточно, чтобы сделать наш автоматизированный UAT ломким.

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

За этим «API» на наших страницах мы заключаем механизм реализации поведения. Здесь мы находим наши выражения XPath, наши селекторы CSS и типы наших виджетов. Таким образом, когда общий элемент появляется на 20 страницах (и, следовательно, возможно, во многих или более тестах), изменение этого элемента (возможно, от ссылки на кнопку) необходимо выполнить только один раз в отношении набора тестов.

Другими словами, Page Objects предоставляют нам «Страхование арахиса от хрупкости».
Среда

автоматизации браузера

. Последний ключевой элемент этой головоломки — это среда автоматизации браузера. Эта часть просуществовала дольше всего, а открытые среды, такие как Selenium, появились еще в 2004 году. За последние пару лет эта экосистема стала невероятно разнообразной, и регулярно появляются новые решения. Некоторые из наиболее популярных сред включают
Selenium ,
Selenium 2 / WebDriver и
Watir . Фреймворки, которые не управляют реальным браузером, но имитируют поведение браузера, также распространены, причем
HtmlUnit является одним из лидеров в этом пространстве. Более подробную информацию о автоматизации браузера см
эти
статьи .

Резюме

Объединение этих трех строительных блоков вместе для составления решения проблемы исполняемой спецификации является мощным предложением. Платформы BDD делают наши тесты похожими на спецификации требований, с которыми наши бизнес-аналитики и заинтересованные стороны чувствуют себя как дома. Современные системы автоматизации браузеров позволяют нам тестировать все более насыщенные веб-интерфейсы, которые находятся в стадии разработки. Объекты Page обеспечивают критический клей, который соединяет мир тестера с миром разработчика. При этом они позволяют нам применять к нашим тестам такое же дисциплинированное мастерство программного обеспечения (модульность, повторное использование, DRY, инкапсуляция и т. Д.), Которое мы применяем к нашему производственному коду. Это мастерство приводит нас к тестовому набору, который читает как проза и мгновенно скажет нам, если мы закончили.