Я напишу, как можно адаптировать популярный язык написания тестов Gherkin для русскоязычных проектов без использования сторонних библиотек, а также поделюсь своим опытом использования этого подхода.
Статья — не перевод, она от начала и до конца является описанием моей точки зрения. Тем не менее, прийти к ней мне помог блог Стива Сандерсона (Steve Sanderson). Рекомендую прочитать его статьи, помеченные тегом «Testing».
TDD-подход в разработке ПО заслуженно завоевал свое место под солнцем. В течение жизни он постепенно переосмыслялся, переходя из разряда методов для поиска багов в разряд методов для описания архитектуры приложения. Следующим шагом, органично дополняющим эволюционировавший TDD является BDD — Behavior Driven Development.
Суть BDD — в описании системы архитектуры приложения в терминах эксперта предметной области, а не программиста, что позволяет ускорить процесс получения обратной связи и убрать традиционные языковые барьеры между создателями ПО и его пользователями.
С помощью BDD тестировать систему (или, как сейчас принято говорить, описывать сценарии взаимодействия) может не только сам программист, пишущий код, но и PM, не разбирающийся в деталях реализации, но хорошо знающий систему с точки зрения пользователя. Для новичков BDD-скрипты — самый простой и натуральный пусть ознакомиться с документацией проекта.
В последнее время BDD применяется чаще в вебе, по большей части из-за того, что его модель сценариев органично вписывается в принцип «запрос-ответ».
Самыми популярнымы инструментами для работы в стиле BDD являются Cucumber для Ruby и SpecFlow для .NET. Оба они используют язык Gherkin. Вот как это выглядит. Разработчик пишет следующий текст:
Для каждого действия он также пишет соответствующие функции:
Таким образом Cucumber или SpecFlow сможет интерпретировать каждый шаг, вычленить с помощью регулярных выражений параметры и запустить соответствующие TestFixtures. В результате мы получаем test-suit, полностью написанный на человеческом языке и понятный каждому участнику проекта.
Для описания сценариев используется шаблон Given/When/Then: Given задают начальные условия, When — операцию, Then — конечный результат. Все сценарии собраны в специальный файл .feature и как несложно догадаться относятся к какой-то определенной фиче проекта.
Ну во-первых тем, что для его использования нужно устанавливать SpecFlow. Тем более, как я опишу чуть ниже, реальная потребность в таком инструменте отсутсвтует, все можно сделать средствами Visual Studio.
Во-вторых, у SpecFlow есть проблемы с русским языком и чтобы корректно настроить его под кириллицу нужно приложить немало усилий. Тут можно возразить, что, мол, все мы айтишники и понимаем английский и что русский в коде нам ни к чему. Это не так. Русский язык позволяет резко увеличить читаемость сценариев и отказаться от мучительного процесса перевода языка предметной области на английский.
Очень просто. С некоторых пор Visual Studio позволяет именовать методы класса с использованием русского языка, по сути это все что нам нужно. Пример выше можно написать так:
Шаблон Given/When/Then трансформируется здесь в Если/Когда/_. По инерции можно было бы переименовать этот шаблон в Если/Когда/Тогда, но написание «Когда я перехожу на главную, я вижу на странице текст „привет“» смотрится намного более органично чем «Когда я перехожу на главную, тогда я вижу на странице текст...». Таким образом, последнее слово «Тогда» я рекомендую просто опускать для улучшения читаемости.
Работает все это крайне просто — каждый тест-класс нужно отнаследовать от базового, в котором определены все необходимые Given/When/Then шаги.
При написании интеграционных тестов я объединяю в тест-классе связанные фичи-регионы, которые в содержат функции-сценарии. Вот как выгладит AccountIntegrationTest:
Если вы уже писали интеграционные тесты (к примеру с использованием WatiN) то вы оцените насколько упростится этот процесс. Вот как раньше выглядел мой сценарий отправки сообщения через форму обратной связи:
А вот как он выглядит сейчас:
В некоторых случаях для выполнения шага ему нужен контекст — результат выполнения предыдущего шага («В_этом_письме_содержится_текст» использует результат работы «Админу_приходит_письмо»). Его также можно спрятать в базовом классе в виде
Но и это еще не все. Также можно значительно упростить работу с таблицами. Как правило именно они делают из интеграционных тестов мелкодисперсную кашу. Вот пример работы со списком загруженных фото в альбоме (загружаются файлы фото, у которых впоследствии можно редактировать названия и описания):
Как видим, с помощью самой IDE можно построить мощное средство тестирования и описания вашего веб-проекта с использованием BDD-подхода. Со временем у вас соберется большая коллекция Given/When/Then-шагов в базовом классе, в помощью которых можно будет построить практически любой сценарий на сайте. Дополняйте вашу библиотеку шагов при необходимости, рефакторите ее так же, как вы рефакторите обычные классы и их методы — эти шаги должны говорить с вами на языке доменной логики.
Статья — не перевод, она от начала и до конца является описанием моей точки зрения. Тем не менее, прийти к ней мне помог блог Стива Сандерсона (Steve Sanderson). Рекомендую прочитать его статьи, помеченные тегом «Testing».
Пролог
TDD-подход в разработке ПО заслуженно завоевал свое место под солнцем. В течение жизни он постепенно переосмыслялся, переходя из разряда методов для поиска багов в разряд методов для описания архитектуры приложения. Следующим шагом, органично дополняющим эволюционировавший TDD является BDD — Behavior Driven Development.
Суть BDD — в описании системы архитектуры приложения в терминах эксперта предметной области, а не программиста, что позволяет ускорить процесс получения обратной связи и убрать традиционные языковые барьеры между создателями ПО и его пользователями.
С помощью BDD тестировать систему (или, как сейчас принято говорить, описывать сценарии взаимодействия) может не только сам программист, пишущий код, но и PM, не разбирающийся в деталях реализации, но хорошо знающий систему с точки зрения пользователя. Для новичков BDD-скрипты — самый простой и натуральный пусть ознакомиться с документацией проекта.
В последнее время BDD применяется чаще в вебе, по большей части из-за того, что его модель сценариев органично вписывается в принцип «запрос-ответ».
Как это выглядит сейчас
Самыми популярнымы инструментами для работы в стиле BDD являются Cucumber для Ruby и SpecFlow для .NET. Оба они используют язык Gherkin. Вот как это выглядит. Разработчик пишет следующий текст:
Scenario: Show logged in user name
Given I am logged in as a user called "Vlad"
When I visit the homepage
Then the page header displays the caption "Здравствуйте, Vlad!"
Для каждого действия он также пишет соответствующие функции:
Given /I am logged in as a user called "(.*)"/ do |name|
create_user(name)
sign_in_as(name)
end
Then /the page header displays the caption "(.*)"/ do |caption|
page_header.should_contain(caption)
end
Таким образом Cucumber или SpecFlow сможет интерпретировать каждый шаг, вычленить с помощью регулярных выражений параметры и запустить соответствующие TestFixtures. В результате мы получаем test-suit, полностью написанный на человеческом языке и понятный каждому участнику проекта.
Для описания сценариев используется шаблон Given/When/Then: Given задают начальные условия, When — операцию, Then — конечный результат. Все сценарии собраны в специальный файл .feature и как несложно догадаться относятся к какой-то определенной фиче проекта.
Чем этот подход плох
Ну во-первых тем, что для его использования нужно устанавливать SpecFlow. Тем более, как я опишу чуть ниже, реальная потребность в таком инструменте отсутсвтует, все можно сделать средствами Visual Studio.
Во-вторых, у SpecFlow есть проблемы с русским языком и чтобы корректно настроить его под кириллицу нужно приложить немало усилий. Тут можно возразить, что, мол, все мы айтишники и понимаем английский и что русский в коде нам ни к чему. Это не так. Русский язык позволяет резко увеличить читаемость сценариев и отказаться от мучительного процесса перевода языка предметной области на английский.
Как это можно сделать по-другому
Очень просто. С некоторых пор Visual Studio позволяет именовать методы класса с использованием русского языка, по сути это все что нам нужно. Пример выше можно написать так:
[TestMethod]
public void Отображение_имени_залогиненного_пользователя()
{
Если_я_залогинен_как("Vlad");
Когда_я_перехожу_на_главную_страницу();
Я_вижу_на_странице_текст("Здравствуйте, Vlad!");
}
Шаблон Given/When/Then трансформируется здесь в Если/Когда/_. По инерции можно было бы переименовать этот шаблон в Если/Когда/Тогда, но написание «Когда я перехожу на главную, я вижу на странице текст „привет“» смотрится намного более органично чем «Когда я перехожу на главную, тогда я вижу на странице текст...». Таким образом, последнее слово «Тогда» я рекомендую просто опускать для улучшения читаемости.
Работает все это крайне просто — каждый тест-класс нужно отнаследовать от базового, в котором определены все необходимые Given/When/Then шаги.
При написании интеграционных тестов я объединяю в тест-классе связанные фичи-регионы, которые в содержат функции-сценарии. Вот как выгладит AccountIntegrationTest:
Если вы уже писали интеграционные тесты (к примеру с использованием WatiN) то вы оцените насколько упростится этот процесс. Вот как раньше выглядел мой сценарий отправки сообщения через форму обратной связи:
[TestMethod]
public void Отправка_сообщения_с_заголовком_и_текстом_без_имейла()
{
string caption = U.GetRandomString();
string text = U.GetRandomString();
using (IE ie = new IE())
{
U.Logout(ie);
ie.GoToContacts();
ie.TextField("Caption").Value = caption;
ie.TextField("Text").Value = text;
ClickOnSendMessage(ie);
ie.Text.ShouldContain("Сообщение успешно отправлено");
Div div = U.GetLastUnreadMailMessageText(ie);
div.Text.ShouldContain(text);
}
}
А вот как он выглядит сейчас:
[TestMethod]
public void Отправка_сообщения_с_заголовком_и_текстом_без_имейла()
{
Если_я_не_залогинен();
Если_я_нахожусь_на_странице_обратной_связи();
Когда_я_печатаю_текст_в_поле("Заголовок", "Заголовок для обратной связи");
Когда_я_печатаю_текст_в_поле("Текст", "Сообщение для обратной связи");
Когда_я_кликаю_по_кнопке_с_надписью("Отправить");
Я_вижу_сообщение("Сообщение успешно отправлено");
Админу_приходит_письмо();
В_этом_письме_содержится_текст("Сообщение для обратной связи");
}
В некоторых случаях для выполнения шага ему нужен контекст — результат выполнения предыдущего шага («В_этом_письме_содержится_текст» использует результат работы «Админу_приходит_письмо»). Его также можно спрятать в базовом классе в виде
protected object _lastActionResult;
и обращаться к нему при необходимости.Но и это еще не все. Также можно значительно упростить работу с таблицами. Как правило именно они делают из интеграционных тестов мелкодисперсную кашу. Вот пример работы со списком загруженных фото в альбоме (загружаются файлы фото, у которых впоследствии можно редактировать названия и описания):
Заключение
Как видим, с помощью самой IDE можно построить мощное средство тестирования и описания вашего веб-проекта с использованием BDD-подхода. Со временем у вас соберется большая коллекция Given/When/Then-шагов в базовом классе, в помощью которых можно будет построить практически любой сценарий на сайте. Дополняйте вашу библиотеку шагов при необходимости, рефакторите ее так же, как вы рефакторите обычные классы и их методы — эти шаги должны говорить с вами на языке доменной логики.