понедельник, 21 января 2013 г.

Python + BDD = Behave?

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

Давайте напишем простую историю использования в нотации Gherkin.

   Feature: Administrative web interface

     Scenario: Login
          When user opens browser and navigates on the page "site.com"
              and user "admin" enters login and password in the login form 
              and user clicks "Login" 
            Then user should see the administrative web interface 

Посмотрим, что у нас получилось.
В первой строчке мы видим описание функционала, который будет тестироваться.
Далее в этом же файле описываются сценарии использования данного функционала.
Каждый сценарий имеет имя и некоторые логические шаги, в нашем случае это When.. Then.
Т.е. "Когда пользователь делает вот так, то должно быть вот так". Все просто и понятно.

Писать хорошие истории использования - искусство.
В самом деле, написанная здесь история использования ужасна. Давайте разберём почему.
Во-первых, функционал, который мы тестируем, почти не задействован в сценарии, который мы описали. Т.е. на самом деле тестируется механизм авторизации пользователей, а не административный веб интерфейс.
Во-вторых, имя сценария малоинформативно. Лучше написать что-нибудь более подробное и понятное.
И в-третьих, его просто неудобно читать. Прочитать можно, но не удобно. Длина шагов неодинакова, некоторые шаги принимают параметры, другие нет. Например, как мы можем определить, что в браузере открыт именно административный веб интерфейс? Почему механизм определения типа интерфейса остаётся тайной? Мы лишаемся гибкости при проектировании тестов.
Эти недостатки могут быть не так очевидны и заметны, если мы только начинаем писать истории использования для своего проекта. Но чем больше тестов написано, тем больше мы чувствуем необходимость их оптимизации и изменения. И это отнимает наше время. Поэтому давайте сразу напишем хорошую (улучшать можно бесконечно) историю использования:

    Feature: Authentication

        Scenario: Login as a administrator and check link "Accounts Management"
             When user opens browser and navigates on the page "site.com" 
                 and user "admin" enters login and password in the login form
                 and user clicks "Login" button on the login form
               Then user should see the link "Accounts Management"

Остановимся на этом варианте истории использования для первого теста.

Так как мы можем использовать одну и ту же историю использования, применяя различные фреймворки (хотя заранее зная фреймворк, можно создавать истории использования, адаптированные именно под него, так удобнее), давайте сделаем краткий обзор популярных фреймворков на Java и Python.

Cucumber - о нём слышали все, даже те, кто тестирует только руками. Очень популярный инструмент BDD, на Ruby. Его можно использовать в тестах, написанных на Ruby, Java, .NET (на самом деле поддерживается более 40 языков программирования, с которыми этот фреймворк может работать)

Lettuce - довольно популярный фреймворк на Python. Очень даже интересный инструмент. Что не обнаружил - возможность создавать отчёт в формате TestNG / JUnit, что существенно усложняет интеграцию с системами CI (например, Jenkins). Опций командной строки маловато, хотя если вам нужен простой инструмент, возможно, это то что нужно.

easyb - популярный фреймворк на Java. Название намекает на простоту его использования, не без основательно.

Freshen - простой и популярный фреймворк на Python. Существенный плюс - нативная интеграция с nosetests, так при взаимодействии с сервером непрерывной интеграции проблем не будет. nosetests сам сделает отчёт и позволит всё настроить так, как вам хочется. К сожалению, этот существенный плюс является так же и минусом, потому что анализ тестов, завершаемых с ошибками, не так прост, потому что nosetests перехватывает вывод ваших python тестов. Так же, например, в отличии от Lettuce и Behave, здесь нет переменных, передаваемых от шага к шагу, что может быть проблемой, если мы хотим сохранять сессию в течении всего теста (например, передавать от шага к шагу экземпляр web driver).

jDave - не очень свежий, но довольно популярный фреймворк на Java. Много хороших отзывов.

Behave - сейчас этот фреймворк всё больше набирает популярности. Есть несколько приятных особенностей этого фреймворка, которые не оставят вас равнодушными. Умеет интегрироваться с серверами непрерывной интеграции и выдаёт красивые, аккуратные и понятные отчеты. Есть клон на Java. Так же из плюсов - это единственный фреймворк, размещенный на python.org.

jBehave - очень популярный фреймворк на Java. Есть его копия для Python, так что это только добавляет популярности. Специалисты, использующие этот фреймворк, легко разберутся с настройкой и использованием Behave на Python.

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

Мы выбрали Python + Behave, и причин для этого много. Мы любим легко расширяемые решения, которые можно настраивать под конкретные задачи. Мы больше знаем Python, чем Java (и любим его больше), что тоже стало решающим фактором.
Что мы получили в результате:
- Тесты пишутся быстро, поддерживать тесты в актуальном состоянии очень просто.
- Легко добавили новые тесты в настроенную систему непрерывной интеграции на Jenkins.
- Количество кода сократилось очень существенно. Тесты используют одни и те же шаги, и даже при переходе от одного функционала к другому для одного и того же продукта шаги почти не изменяются (конечно, для этого надо постараться написать хорошие сценарии), чтобы потом многократно все не исправлять.
Из минусов можно отметить то, что сложные функциональные тесты таким подходом автоматизировать не очень удобно. Приходится поддерживать несколько типов тестов одновременно, и мы сейчас решаем задачу оптимизации наших функциональных тестов.