Функциональное тестирование веб-приложений без боли

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

Вот примерно такая же история с автоматическим функциональным(приёмочным) тестированием. О такой классной штуке как автоматические тесты писал ещё Сам Кент Бек. Ну, а автоматические функциональные тесты — это вообще лакомый кусок для современных agile методик разработки ПО. Например, тот же Scrum — включает в себя практику «Демо», в ходе которой заказчику нужно показать развитие продукта, осуществлённое в ходе итерации.
Я, конечно, не спец agile-практик, и не изучал рынок инструментов для функционального тестирование веб проектов — возможно, и раньше в этом сегменте всё было зашибись. Но за те 5 лет, что я работаю программистом — я всего лишь пару раз слыхал такие слова автоматическое функциональное тестирование, Selenium и ни разу не видел применения на практике.
Так вот, возвращаясь к лирическому вступлению, мне кажется, что то колоссальное качественное изменение как раз и произошло недавно. И есть ощущение, что в ближайшее время только ленивый пренебрежет функциональным тестированием своего веб проекта.
Что же собственно произошло? Я подписан на RSS-ку блога Springsource, и однажды обнаружил статью с вот таким интригующим названием — The future of functional web testing?.
Инструменты Geb и Spock, описанные в данной статье, меня зацепили, и я решил попробовать.

Собственно далее, я постараюсь наступить на горло песне прервать приступ графоманства и описать простой пример с использованием данных инструментов.

Установка инфраструктуры


Для проведения тестов нам понадобится:


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


В данном параграфе будет приведён пример автоматизации простого, но несомненно полезного функционального теста.
Суть функционального теста будет состоять в том, чтобы:
— войти на сайт Википедии
— набрать в поиске «Функциональное тестирование»
— убедиться в том, мы действительно попали на желаемую страницу
(например, мы поищем вхождение в текст страницы строки «это тестирование ПО в целях проверки реализуемости функциональных требований»)

Структура каталога проекта


Структура каталога проекта для тестирования выглядит следующим образом:


pom.xml — файл с описанием проекта в Maven
simplefunctest — пакет, в котором будут храниться классы для описания тестов

Собственно Тест


package my.tests.simplefunctest

import geb.spock.GebSpec

class MyFirstSpec extends GebSpec {

 def "test search functional testing wiki page"() {
  given: "we are at main wiki page"
  to MainWikiPage

  when: "try to search functional testing page"
  searchField.value("Функциональное тестирование")
  searchButton.click()

  then: "check we are on functional testing page"
  at FunctionalTestingWikiPage
 }
}

* This source code was highlighted with Source Code Highlighter.

Что здесь происходит?
— Мы создаём класс для пакета наших тестов, наследуемся от базового класса, описывающего пакет тестов GebSpec.(По идее здесь должна использоваться терминология BDD, а именно спецификации и т.д., но для краткости и понимания буду её опускать)
class MyFirstSpec extends GebSpec { ... }

* This source code was highlighted with Source Code Highlighter.

— Мы создаём описание конкретного теста, предназначенного для поиска страницы, посвященной функциональному тестированию, на сайте Википедии
def "test search functional testing wiki page"() { ... }

* This source code was highlighted with Source Code Highlighter.

— Мы оказываемся на главной странице Википедии
given: "we are at main wiki page"
to MainWikiPage


* This source code was highlighted with Source Code Highlighter.

— Мы вводим в поле поиска фразу «функциональное тестирование» и инициируем нажатие кнопки поиска
when: "try to search functional testing page"
searchField.value("Функциональное тестирование")
searchButton.click()


* This source code was highlighted with Source Code Highlighter.

— Затем мы проверяем, что действительно находимся на странице Википедии, посвященной функциональному тестированию
then: "check we are on functional testing page"
at FunctionalTestingWikiPage


* This source code was highlighted with Source Code Highlighter.


Описание главной страницы Википедии


package my.tests.simplefunctest

import geb.Page

class MainWikiPage extends Page {
 static url = "http://ru.wikipedia.org/"
 static at = {title == "Википедия — свободная энциклопедия"}
 static content = {
  searchField { $("input", id: "searchInput")}
  searchButton (to: FunctionalTestingWikiPage) { $("button", id: "searchButton")}
 }
}

* This source code was highlighted with Source Code Highlighter.

Что здесь происходит?
— Мы создаём класс для описания главной страницы Википедии, наследуемся от базового класса, описывающего страницу Page
class MainWikiPage extends Page { ... }

* This source code was highlighted with Source Code Highlighter.

— Указываем URL страницы(требуется, так как с этой страницы начинается тест)
static url = "http://ru.wikipedia.org/"

* This source code was highlighted with Source Code Highlighter.

— Описываем замыкание для проверки, что мы находимся на требуемой странице(в данном случае проверяем, что заголовок представляет собой «Википедия — свободная энциклопедия»)
static at = {title == "Википедия — свободная энциклопедия"}

* This source code was highlighted with Source Code Highlighter.

— Описываем замыкание для наполнения страницы(в данном случае это — текстовое поле поиска и кнопка для осуществеления поиска(оба элемента будут найдены по тегу и id))(см. статью про использование функции $())
static content = {
  searchField { $("input", id: "searchInput")}
  searchButton (to: FunctionalTestingWikiPage) { $("button", id: "searchButton")}
}


* This source code was highlighted with Source Code Highlighter.

Описание страницы результата поиска


package my.tests.simplefunctest

import geb.Page

class FunctionalTestingWikiPage extends Page {

 static at = { $().text().contains("это тестирование ПО в целях проверки реализуемости функциональных требований") }
}

* This source code was highlighted with Source Code Highlighter.

Надеюсь тут всё понятно.

Собственно старт теста


Теперь перейдём к самому интересному, ради чего мы всё это затеяли, а именно к старту тестов.
В командной строке, в корневом каталоге проекта нужно выполнить:
mvn clean test

* This source code was highlighted with Source Code Highlighter.

Бинго! У вас должен стартовать FireFox(он настроен как браузер по умолчанию для тестов) и выполнить(без вашего участия(!!!)) то, что мы задумали.

Возможно, Firefox вам будет недостаточно и вы заходите прогнать тест в IE(для этого настроен специальный профиль):
mvn clean test

* This source code was highlighted with Source Code Highlighter.

Или может быть в Chrome:
mvn clean test -P chrome

* This source code was highlighted with Source Code Highlighter.

Заключение


Я не буду обременять ни себя, ни вас описанием библиотек-их есть у него в достаточном количестве на сайтах этих библиотек, а просто порекомендую забрать из репозитория проект и попробовать самостоятельно.

Важные ссылки


Поделиться публикацией

Комментарии 19

    0
    Интересная вещь, очень напомнила Огурец — habrahabr.ru/blogs/ruby/62958/
    Сравнивали с ней, какие отличия/преимущества/недостатки?
      +1
      Общее в них — тестирование в стиле BDD.

      Различия — в уровнях:

      cucumber — в первую очередь для модульного / интеграционного тестирования без UI.

      geb заточен под тестирование на уровне UI, то есть либо функционального (приемочного) тестирования полного среза приложения, либо UI-слоя с заглушками для остальных слоев.
        +1
        Нет, не сравнивал. Спасибо за ссылку.
          0
          В посте я нигде не отметил, но видимо стоит:
          Spock предназначена для проведения тестирования в стиле BDD, а Geb позволяет проводить автоматизацию работы с браузером на основе Selenium/WebDriver.
          Никто не мешает их использовать отдельно.
          +1
          Более подробно про Огурец (Cucumber) можно прочитать тут — cukes.info/ (англ.)
          Меня смущает Java в Ged.
            0
            Возможно, у вас есть на то причины.:)
            Но, как мне кажется, ради такого простого и мощного DSL можно и не обращать на то, что где-то внутри Java.
            У меня таких причин нет — пишу в основном на Java, иногда Groovy.
              0
              То есть язык на котором пишутся тесты даже не Java, знания которой может пригодится, а что-то специфичное для этого инструмента?
                0
                Вообще говоря, для написания тестов используется DSL, предоставляемый Geb и Spock. А в его основе используется Groovy.
                  0
                  А в основе Groovy — Java…

                  Эх… Ясно, что дело тёмное :(
            +1
            pom.xml )) кого еще мучают оптические иллюзии?
              0
              я тоже подумал что это шутка такая у автора
              +1
              1. Сколько времени вы потратили на написание теста?
              2. Сколько времени занимает выполнение теста руками?
              3. Как часто вы будете повторять тест?

              X = (Время написания/отладки теста) + (Количество выполнений) * (Время прохождения автоматизированного теста)
              Y = (Количество выполнений) * (Время ручного тестирования)

              X
                0
                Хотел написать в конце: (X < Y)?
                  0
                  если «написал и забыл» то — скорей всего Y < X
                  если проект развивается, и тем более заказчик меняет задачи каждые 2 недели (по agile (да и по жизни это сплошь и рядом)) — Y > (X*10)
                    +1
                    Вот и к чему я всё это спрашивал. Не всегда и не все тесты есть смысл автоматизировать.
                      +1
                      Факт. Но если есть смысл автоматизировать — то грех этого не делать.
                        0
                        Только надо учитывать, что определение есть или нет смысла автоматизировать (не важно что) — процесс очень субъективный, по крайней мере пока нет инструментов автоматизации с «интуитивно понятным интерфейсом» и, что немаловажно, «интуитивно понятным» процессом установки/администрирования. Там где для одного человека достаточно, например, того, что есть url, по которому можно скачать jar-файл, для другого надо писать многостраничный мануал.
                          0
                          Конечно, субъективный. Всё очень зависит от конкретного проекта и команды над ним работающей.
                  +1
                  1. Не засекал, но я думаю размеры классов говорят, что это не заняло много времени.
                  2. Зависит от сложности теста. Но вам самому приятно руками раз за разом выполнять тест?
                  3. Зависит от проекта, от практик, внедрённых в процесс разработки. Но если речь о функциональном тестировании, то скорее всего этот тест будет повторяться за время существования проекта несколько десятков, а может и сотен раз.

                  Естественно, я первоначально потратил время на изучение Geb и Spock. Естественно, возникали вопросы и проходилось усиленно гуглить, изучать обсуждения в mailing list'ах.

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

                Самое читаемое