Pull to refresh

Request spec in Action

Reading time7 min
Views5.9K
Тестирование стало неотъемлемой частью любой разработки программного продукта, будь то приложение под настольный компьютер, мобильное устройство или web. Уже никто не отрицает важность это этапа и последствий, которые принесет его отсутствие. Среди них большое время на проверку каждого элемента (страницы), и неожиданные сюрпризы в поведении продукта, увеличение затрат на исправление программы. Принцип написания тестов достаточно прост – «желтый цвет», «красный цвет», «зеленый цвет», рефакторинг. Где желтый цвет – это не созданный тест (pending), красный цвет – не прошедший тест, а зеленый – системе работает как надо.

Для каждого вида программирования, существует множество типов тестирования. Но есть и общие моменты, присутствующие везде. Так как основной род моей работы является создание web приложений под ROR, поговорим про особенности тестирования данных приложений.


Настройка среды


Вы можете найти множество сред для тестирования (Test Unit, Rspec, Cucumber & etc), но лично я активно использую Rspec. Он позволяет придать тестам красивый вид, но при этом сохранить вид кода. В gemfile добавим новую группу:

group :development, :test do
gem "rspec-rails", ">= 2.5"
end


Вы можете удивиться и спросить: «Зачем добавлять rspec в development режим?» Все достаточно просто – rspec добавляет при генерации model, controller, scaffold соответствующие spec-и. Установив gem, проведем последнюю команду установки:

rails g rspec:install

Теперь мы можем запускать наши тесты по команде rake rspec. Отрицательная сторона данного подхода заключается в том, что при малейшем изменении нашего проекта нам снова и снова придется выполнять эту команду, которая будет продергивать абсолютно все тесты. Отсюда следует, что необходимо найти какой-нибудь watcher, чтоб он вместо нас выполнял необходимые команды. На данный момент я знаю про два неплохих решения – autotest и guard. Autotest пришел в rails с других framework, хорошо зарекомендовав себя, и раньше я использовал только его. Но недавно я познакомился с прелестным решением в виде guard. Сам по себе guard не несет в себе необходимый функционал, но при использовании его с дополнениями получается очень шикарная вещь. Этих дополнений у guard-а уже около 2 десятков, и они несут в себе огромные возможности. Однако остановимся пока на одном – guard-rspec. Установим необходимые файлы:

group :test do
gem ‘guard-rspec’
end


Выполним команду инициализации:

guard init .
guard init rspec


Первая создаст guardfile (файл конфигурации наблюдателя), вторая добавит специфичные для rspec действия. Теперь по команде guard у вас работает watcher, готовый в любой момент продернуть необходимый spec. Но лог у данного решения достаточно скучный, поэтому добавим ему немного красок. Сперва, установим библиотеки уведомлений. И тут мы сталкиваемся с первыми проблемами – у вас с напарником разные ОС (Mac OS и Linux), а тестировать нужно всем. Выходом для нас послужило следующее решение:

gem 'libnotify' , :require => false if RUBY_PLATFORM =~ /linux/i
gem 'rb-inotify', :require => false if RUBY_PLATFORM =~ /linux/i
gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i


Далее, заменим все точки в rspec на процентное исчисление:

gem 'fuubar'

И, наконец, в Guardfile пропишем следующее:

guard 'rspec', :cli =>'--format Fuubar --color'

Запустим прохождение тестов. В консоли получаем красивую строку прогресса, а при завершении теста — всплывающее окно.

Следующим шагом для настройки среды тестирования становиться придание чистоты нашим тестам. Ведь никто не хочет, чтобы при выполнении тестов нам попадались значения и записи в базе данных от предыдущих проверок. Хорошим выбором в данном случае может быть gem database_cleaner. Его конфигурирование приведено ниже:

config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end

config.before(:each) do
DatabaseCleaner.start
end

config.after(:each) do
DatabaseCleaner.clean
end


После настройки чистильщика, неплохо поговорить про создание тестируемого контента. Rspec предлагает использовать mock-и, но лично я предпочитаю генерировать реальные объекты. Для создания различных объектов можно использовать machinist или factory_girl. С machinist я особо не знаком, поэтому ничего про него сказать не могу. Но вот с factory_girl работаю достаточно давно, и нареканий gem никогда не вызывал. Добавим наши gem-ы в группу test:

group :test do
gem ‘guard_rspec’
gem ‘factory_girl_rails’
gem ‘database_cleaner’
end


Остальное использование я предлагаю рассмотреть вне этой статьи по ссылке

Само тестирование


Установив базовую среду тестирования, можно поговорить и про его особенности. Rspec разделяет все тестирование по частям, то есть в папке spec создаются копии папок раздела app. За мой скромный период разработки и тестирования, я нашел целесообразным только разделы models, helpers, mailers. И тут же возникает вопрос: «А что делать с важнейшими составляющими controllers и views?» На мой взгляд, решение, которое предлагает rspec, не совсем полноценно, и оно создает не совсем реальную ситуацию происходящего. Поэтому для своей разработки я выбрал тестирование запросов и полученных ответов. Для этого установим capybara. Автор библиотеки (Jonas Nicklas) описывает её так:

«Capybara aims to simplify the process of integration testing Rack applications, such as Rails, Sinatra or Merb. Capybara simulate show a real user would interact with a web application.»

Что дословно звучит как:

«Морская свинка Capybara упрощает процесс комплексного тестирования, симулируя поведения пользователя в вашем приложении»

Добавим его в gemfile. После в файле spec/test_helper.rb нужно добавить библиотеки Capybara:

require 'capybara/rspec'

Теперь наши тесты наполняются многими функциональными и удобными методами тестирования и манипуляцией с виртуальной страницей. Начнем по порядку:

Посещение страницы выполняется понятным методом visit:

visit ‘/’

Кроме строчных адресов, метод принимает rails path helper:

visit car_wheels_path(car)

Но основная функциональность библиотеки заключается в доступном объекте page. Данный объект представляет собой виртуальную страницу для тестирования. Для получения её содержимого необходимо лишь обратится к методу body:

puts page.body

Давайте пройдемся по списку команд манипуляций. Текстовые и парольные поля заполняются методом fill_in, который принимает первый параметром id, class, name элемента, а параметр with задает содержимое:

fill_in ‘car_manufacture’, :with=>’Audi’
fill_in ‘car[model]’, :with=>’A4’


Для выбора варианта в select используется метод select (спасибо, Кеп), который сначала принимает значение, а потом уже элемент для манипуляции:

select ‘Silver’, :from=>’car[color]’

Также существуют методы check, uncheck, choose, цель которых – изменение состояния checkbox и radio button. Они принимают лишь идентификатор элемента:

check ‘car[full_package]’
choose ‘car[year][2010]’


Но бывает так, что в странице существует несколько элементов с одинаковыми name, или, не дай Бог, id. Для этого существует блоковый метод within, который ограничивает поиск внутри указанного элемента:

within ‘form#payment_card’ do
#много манипуляций
end


Для нажатия кнопок и ссылок существует несколько методов — универсальный (click_on) и специфические (click_link, click_button):

click_on ‘submit_form’
click_link ‘read_more’


Теперь перейдем к проверки полученной структуры. Для проверки присутствия DOM элемента присутствует метод have_selector:

page.should have_selector('table tr')

А что если необходимо проверит количество этих элементов? То и тут все достаточно просто – добавим параметр count:

page.should have_selector(‘table tr’, :count=>3)

В ситуациях, когда элемент сам не фажен, а важно лишь содержимое пригодится метод page.have_content?

page.should have_content(“Audi club”)

А как же js?


Написав пару spec-ов, вы уже заметили, что capybara полностью игнорирует js скрипты. Этот шаг разработчики сделали из-за сохранения быстродействия при прохождении тестов. Когда же есть необходимость проверить нашу страницу с полным функционалом, задействуем параметр js spec-а:

it ‘should have many js’, :js=>true do
visit …
end


Пред нами появится окно любимого браузера, откроется тестируемая страница, и курсор сам проделает все движения. Просто магия, но давайте разберемся. На самом деле capybara просто переключил web driver для обработки тестовой страницы. По умолчанию используется rack driver, который оперирует только полученным с ответом контентом. При использовании параметра js capybara начинает обращаться с selenium driver. Selenium запускает системный браузер, если не указан другой, и выполняет все желаемые действия. Все хорошо и красиво, кроме пары моментов. Во-первых, время выполнения теста становится очень долгим. И не удивительно, кроме загрузки самой среды rails, тест запускает браузер, плюс выполнение манипуляций также требует определённого времени. Во вторых, меня не устраивало постоянное отображение браузера. Поэтому мне пришлось отказаться от selenium в пользу альтернативного web driver-а – capybara-webkit. Данный gem специально писался под решение данных проблем. Для установки библиотеки, кроме самого capybara-webkit, необходимо установить ещё qt библиотеки. После в spec_helper укажем какой driver использовать для страниц с javascript:

Capybara.javascript_driver :webkit

Теперь интегральные тесты capybara будет выполнять быстро и без лишней анимации.

Другим интересным моментом, с которым вы столкнётесь, будет проверка элементов, которые загружаются c помощью ajax. Методы have_selector и have_content не всегда проходят, поэтому используем такие методы как all, find, first:

first(‘div.info’).should be

all(‘tr.ads’).should have(3).items


Также методы find и first могут пригодиться для эмуляции кликов на элементы отличные от кнопок и ссылок.

first(‘div.catalog’).click
find(‘p#info).click


Если вам захотелось выполнить какой-нибудь скрипт, то capybara и тут вам пригодится:

page.execute_javascript “alert(‘Hello world’)”

Первоначально это кажется полной бессмыслицей – «Ну какой смысл выполнять в тестах js?» И ответом станет ситуация, когда необходимо проследить поведение страницы на действия отличные от click и select:

page.execute_javascript ‘$(“p.long_text”).hover();’

Вот так мы проэмулировали наведение на параграф курсора.

Пару наблюдений из жизни

И в конце моего поста пара советов из личного опыта. При использовании capybara, database_cleaner и factory_girl с любым js web driver, созданные ресурсы не буду отображаться на тестовой странице. Исправить данную ситуацию следующая настройка:

config.use_transactional_fixtures = false

Ещё одним интересным трюком можно выделить фокусировку в rspec, эффективность которой неоспорима в ресурсоёмких тестах. Настройка фокусировки проходит следующим образом. Сначала добавим в файл конфигурации среды тестирования строки:

config.filter_run :focus=>true
config.run_all_when_everything_filtered = true


После, для выполнения интересующих действий, достаточно будет добавить именной параметр :focus=>true и список spec ов будет отфильтрован.

Полезные ссылки


Capybara
Capybara Webkit
Data base cleaner
Factory girl rails
Guard
Tags:
Hubs:
Total votes 9: ↑7 and ↓2+5
Comments17

Articles