|
Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
Войти, выйти
Введение в Cucumber (опционально)
Закончив основу системы аутентификации примера приложения, мы собираемся воспользоваться подвернувшейся возможностью для того чтобы показать как написать тесты входа с помощью Cucumber - популярного инструмента для разработки через поведение, который пользуется значительной популярностью в сообществе Ruby. Этот раздел необязателен и может быть пропущен без потери целостности повествования.
Cucumber позволяет определять текстовые истории описывающие поведение приложения. Множество Rails программистов находят Cucumber особенно полезным при работе над клиентскими проектами; поскольку они могут быть прочитаны даже технически не подкованными пользователями, тесты на Cucumber могут быть прочитаны (а иногда даже написаны) клиентом. Конечно же, применение тестового фреймворка, который не является чистым Ruby, имеет и оборотную сторону, и я считаю что текстовые истории зачастую могут быть излишне многословными. Тем не менее, Cucumber занял прочные позиции в Ruby-инструментарии тестирования и мне особенно нравится его акцент на поведении верхнего уровня, а не на деталях реализации.
Поскольку акценты в этой книге смещены в сторону RSpec и Capybara, последующая презентация совершенно не претендует на полноту и исчерпывающее раскрытие темы. Ее цель - просто дать вам возможность ощутить вкус Cucumber-а (несомненно свежий и сочный) — если он поразит ваше воображение, существуют целые книги на эту тему готовые удовлетворить ваш аппетит. (Я особенно рекомендую The RSpec Book (David Chelimsky) и Rails 3 in Action (Ryan Bigg и Yehuda Katz), и The Cucumber Book (Matt Wynne и Aslak Hellesoy).)
Установка и настройка
Для того чтобы установить Cucumber, во-первых, добавьте гем cucumber-rails и служебный гем database_cleaner в группу :test в Gemfile (Листинг 8.31).
. . . group :test do . . . gem 'cucumber-rails', '1.4.0', :require => false gem 'database_cleaner', github: 'bmabey/database_cleaner' end . . .Листинг 8.31. Добавление гема cucumber-rails в Gemfile.
Затем установите как обычно:
$ bundle install
Для того чтобы настроить приложение для использования Cucumber, мы затем генерируем несколько необходимых, поддерживающих его работу файлов и директорий:
$ rails generate cucumber:install
Это создает директорию features/ где будут жить файлы связанные с Cucumber.
Фичи и шаги
Огурцовые фичи это описания ожидаемого поведения с помощью plain-text языка называемого Gherkin. Gherkin тесты читаются во многом как хорошо написанные примеры RSpec, но, поскольку они написаны простым текстом, они более доступны для тех, кому комфортнее читать английский, а не код Руби.
Наши Огурцовые фичи будут реализовывать небольшое количество примеров входа в Листинге 8.5 и Листинге 8.6. Для того чтобы начать, мы создадим файл signing_in.feature в директории features/.
Огурцовые фичи начинаются с короткого описания функционала:
Feature: Signing in
Затем они добавляют индивидуальные сценарии. Например, для того, чтобы протестировать провальный вход, мы можем написать следующий сценарий:
Scenario: Unsuccessful signin
Given a user visits the signin page
When they submit invalid signin information
Then they should see an error messageАналогично, для того, чтобы протестировать успешный вход, мы можем добавить следующее:
Scenario: Successful signin
Given a user visits the signin page
And the user has an account
When the user submits valid signin information
Then they should see their profile page
And they should see a signout linkСобрав все это вместе мы приходим к файлу Огурцовой фичи показанному в Листинге 8.32.
Feature: Signing in
Scenario: Unsuccessful signin
Given a user visits the signin page
When they submit invalid signin information
Then they should see an error message
Scenario: Successful signin
Given a user visits the signin page
And the user has an account
When the user submits valid signin information
Then they should see their profile page
And they should see a signout link
Листинг
8.32.
Огурцовые фичи для тестирования входа. features/signing_in.feature
Для запуска фич мы используем исполняемую команду cucumber:
$ bundle exec cucumber features/
Сравните это с
$ bundle exec rspec spec/
В данном контексте стоит отметить, что, как и RSpec, Cucumber может быть вызван с помощью Rake-задачи:
$ bundle exec rake cucumber
(По непонятным для меня причинам, это иногда пишут как rake cucumber:ok.)
Все что мы пока сделали, это лишь написали немного простого текста, так что не думаю что для вас стало сюрпризом что Огурцовые сценарии пока не проходят. Для того чтобы получить зеленый набор тестов, нам необходима добавить файл step, который свяжет строки простого текста с Руби-кодом. Файл отправляется в директорию features/step_definitions; мы назовем его authentication_steps.rb.
Строки Feature и Scenario нужны в основном для документации, но каждой последующей строке нужен соответствующий Ruby. Например, строка
Given a user visits the signin page
в файле фич будет обработана соответствующим определением шага
Given /^a user visits the signin page$/ do visit signin_path end
В фиче, Given это просто строка, но в файле с шагами Given является методом который принимает регулярное выражение и блок. Регулярное выражение соответствует тексту строки в сценарии, а содержимое блока является чистым Руби кодом, необходимым для реализации шага. В данном случае, "a user visits the signin page" реализуется посредством
visit signin_path
Если это выглядит знакомым, все правильно: это просто Capybara, которая включена по умолчанию в файлы с Огурцовыми шагами. Следующие две строки тоже должны выглядеть знакомо; шаги сценария
When they submit invalid signin information Then they should see an error message
в файле фич обрабатываются следующими шагами:
When /^they submit invalid signin information$/ do
click_button "Sign in"
end
Then /^they should see an error message$/ do
expect(page).to have_selector('div.alert.alert-error')
endПервый шаг также использует Capybara, при этом второй использует объект Capybara page вместе с RSpec. Очевидно, вся работа с тестами, которую мы проделали с RSpec и Capybara также полезна с Cucumber.
Остальные шаги обрабатываются аналогично. Конечный файл определения шагов преставлен в Листинге 8.33. Попробуйте добавлять шаги по одному, запуская
$ bundle exec cucumber features/
каждый раз до тех пор пока все тесты не пройдут.
Given /^a user visits the signin page$/ do
visit signin_path
end
When /^they submit invalid signin information$/ do
click_button "Sign in"
end
Then /^they should see an error message$/ do
expect(page).to have_selector('div.alert.alert-error')
end
Given /^the user has an account$/ do
@user = User.create(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar")
end
When /^the user submits valid signin information$/ do
fill_in "Email", with: @user.email
fill_in "Password", with: @user.password
click_button "Sign in"
end
Then /^they should see their profile page$/ do
expect(page).to have_title(@user.name)
end
Then /^they should see a signout link$/ do
expect(page).to have_link('Sign out', href: signout_path)
end
Листинг
8.33.
Завершенные шаги, необходимые для прохождения фич входа. features/step_definitions/authentication_steps.rb
С кодом в Листинге 8.33, Огурцовые тесты должны пройти:
$ bundle exec cucumber features/
Контрапункт: кастомные проверки RSpec
Написав несколько простых Огурцовых сценариев, стоит сравнить результат с эквивалентным примером на RSpec. Для начала, взглянем на Огурцовую фичу в Листинг 8.32 и соответствующее определение шагов в Листинге 8.33. Затем взглянем на RSpec request specs (интеграционные тесты):
describe "Authentication" do
subject { page }
describe "signin" do
before { visit signin_path }
describe "with invalid information" do
before { click_button "Sign in" }
it { should have_title('Sign in') }
it { should have_selector('div.alert.alert-error') }
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before do
fill_in "Email", with: user.email.upcase
fill_in "Password", with: user.password
click_button "Sign in"
end
it { should have_title(user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should_not have_link('Sign in', href: signin_path) }
end
end
endВы можете видеть, что дело может быть сделано как с помощью Огурца, так и с помощью интеграционных тестов. Огурцовые фичи легкочитаемы, но они полностью отделены от кода который их реализует, а это палка о двух концах. Я считаю что Огурец легок в чтении, но сложен в написании, в то время как интеграционные тесты (для программистов) немного более сложны в чтении и намного более просты в написании.
Один из замечательных эффектов Огурцового разделения является то, что он работает на более высоком уровне абстракции. Например, мы пишем
Then they should see an error message
для того, чтобы выразить ожидание увидеть сообщение об ошибке, и
Then /^they should see an error message$/ do
expect(page).to have_selector('div.alert.alert-error')
endдля того чтобы реализовать тест. Что особенно удобно в этом, так это то, что только второй элемент (шаг) зависит от реализации, таким образом, если мы изменим, например, класс CSS используемый для сообщений об ошибках, файл фич останется неизменным.
В этом случае можно запечалиться переписывая
should have_selector('div.alert.alert-error')в куче мест, в то время как вы на самом деле хотели лишь указать на то, что на странице должно присутствовать сообщение об ошибке. Эта практика тесно связывает тест с реализацией и мы должны были бы изменить это повсеместно при изменении реализации. В контексте чистого RSpec для этого есть решение, которое заключается в использовании кастомных проверок, позволяющих нам писать вместо этого следующее:
should have_error_message('Invalid')Мы можем определить такие проверки в том же вспомогательном файле, в который мы поместили тестовый хелпер full_title в Разделе 5.3.4. Сам код выглядит примерно следующим образом:
RSpec::Matchers.define :have_error_message do |message|
match do |page|
expect(page).to have_selector('div.alert.alert-error', text: message)
end
endМы можем также определить вспомогательные функции для общепринятых операций:
def valid_signin(user) fill_in "Email", with: user.email fill_in "Password", with: user.password click_button "Sign in" end
Получившийся в результате вспомогательный код показан в Листинге 8.34 (который включает в себя результаты Листинга 5.41 и Листинга 5.42 из Раздела 5.6). Я нахожу этот подход более гибким, нежели Огурцовые определения шагов, в особенности когда проверки или помощники долженствования натурально принимают аргумент, такой как valid_signin(user). Определения шагов может повторить эту функциональность с помощью проверок регулярных выражений, но я считаю такой подход гораздо более громоздким (# в оригинале - (cu)cumbersome).
include ApplicationHelper
def valid_signin(user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
RSpec::Matchers.define :have_error_message do |message|
match do |page|
expect(page).to have_selector('div.alert.alert-error', text: message)
end
end
Листинг
8.34.
Добавление вспомогательного метода и кастомной RSpec проверки. spec/support/utilities.rb
С кодом из Листинга 8.34, мы можем написать
it { should have_error_message('Invalid') }и
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { valid_signin(user) }
.
.
.В наших тестах есть множество примеров тесной связи между тестами и реализацией сайта. Прохождение по текущему набору тестов и разрыв связей между тестами и деталями реализации с помощью создания кастомных проверок и методов остается в качестве упражнения (Раздел 8.5).