Опубликован: 27.01.2016 | Доступ: свободный | Студентов: 914 / 58 | Длительность: 23:07:00
Лекция 3:

В основном статические страницы

Автоматизируем тесты с Guard

Одно из неудобств связанных с использованием команды rspec это необходимость переключаться на командную строку и запускать тесты вручную. (Второе неудобство - медленный запуск набора тестов рассматривается в Разделе 3.6.3.) В этом разделе мы покажем как использовать Guard для автоматизации запуска тестов. Guard отслеживает изменения в файловой системе так что, например, когда мы изменяем файл static_pages_spec.rb запустится только этот тест. Более того, мы можем настроить Guard таким образом, что при, скажем, изменении файла home.html.erb автоматически запустится static_pages_spec.rb.

Вначале мы добавим guard-rspec в Gemfile (Листинг 3.34).

source 'https://rubygems.org'
ruby '2.0.0'
#ruby-gemset=railstutorial_rails_4_0

gem 'rails', '4.0.2'

group :development, :test do
  gem 'sqlite3', '1.3.8'
  gem 'rspec-rails', '2.13.1'
  gem 'guard-rspec', '2.5.0'
end

group :test do
  gem 'selenium-webdriver', '2.35.1'
  gem 'capybara', '2.1.0'

  # OS X: раскомментируйте эти строки.
  # gem 'growl', '1.0.3'

  # Linux: раскомментируйте эти строки.
  # gem 'libnotify', '0.8.0'

  # Windows: раскомментируйте эти строки.
  # gem 'rb-notifu', '0.0.4'
  # gem 'win32console', '1.3.2'
  # gem 'wdm', '0.1.0'
end

gem 'sass-rails', '4.0.1'
gem 'uglifier', '2.1.1'
gem 'coffee-rails', '4.0.1'
gem 'jquery-rails', '3.0.4'
gem 'turbolinks', '1.1.1'
gem 'jbuilder', '1.0.2'

group :doc do
  gem 'sdoc', '0.3.20', require: false
end

group :production do
  gem 'pg', '0.15.1'
  gem 'rails_12factor', '0.0.2'
end
Листинг 3.34. Gemfile включающий Guard для примера приложения.

Убедитесь в том что вы раскомментировали строки соответствующие вашей системе в тестовой группе. (Обратите внимание - если вы хотите использовать уведомления Growl, вам придется приобрести Growl, который доступен в Apple App Store по весьма скромной цене.)

Затем мы устанавливаем гемы запустив bundle install:

$ bundle install

Затем инициализируем Guard для работы с RSpec:

$ bundle exec guard init rspec
Writing new Guardfile to /Users/mhartl/rails_projects/sample_app/Guardfile
rspec guard added to Guardfile, feel free to edit it

Теперь отредактируем сгенерированный Guardfile так чтобы Guard запускал правильные тесты после обновления интеграционных тестов и представлений (Листинг 3.35).

require 'active_support/inflector'

guard 'rspec', all_after_pass: false do
  .
  .
  .
  watch('config/routes.rb')
  # Custom Rails Tutorial specs
  watch(%r{^app/controllers/(.+)_(controller)\.rb$}) do |m|
    ["spec/routing/#{m[1]}_routing_spec.rb",
     "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb",
     "spec/acceptance/#{m[1]}_spec.rb",
     (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
                       "spec/requests/#{m[1].singularize}_pages_spec.rb")]
  end
  watch(%r{^app/views/(.+)/}) do |m|
    (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
                      "spec/requests/#{m[1].singularize}_pages_spec.rb")
  end
  watch(%r{^app/controllers/sessions_controller\.rb$}) do |m|
    "spec/requests/authentication_pages_spec.rb"
  end
  .
  .
  .
end
Листинг 3.35. Дополнения к дефолтному Guardfile. Обратите внимание на добавленный require.

Здесь строка

guard 'rspec', all_after_pass: false do

обеспечивает незапуск полного набора тестов после прохождения провальных тестов (для ускорения цикла Красный-Зеленый-Реорганизация).

Теперь мы можем запустить guard следующим образом:

$ bundle exec guard

Для того чтобы избавиться от необходимости запускать команду с префиксом bundle exec, повторите шаги из Раздела 3.6.1.

Кстати, если Guard будет жаловаться на отсутствие директории spec/routing, вы можете исправить ситуацию создав пустую директорию с соответствующим названием:

$ mkdir spec/routing

Ускоряем тесты с помощью Spork

При запуске bundle exec rspec вы могли заметить что перед началом запуска тестов проходит несколько секунд, тогда как выполнение самих тестов происходит довольно быстро. Это связано с тем, что при каждом запуске тестов RSpec должен перезагрузить все Рельсовое окружение. Тестовый сервер Spork 13Spork это комбинация spoon-fork (ложка-вилка). Название проекта обыгрывает использование Spork-ом форков POSIX-а. предназначен для решения этой проблемы. Spork загружает окружение однократно, а затем поддерживает пул процессов для запуска следующих тестов. Spork особенно полезен в комбинации с Guard (Раздел 3.6.2).

Первый шаг это добавление spork гем зависимости в Gemfile (Листинга 3.36).

source 'https://rubygems.org'
ruby '2.0.0'
#ruby-gemset=railstutorial_rails_4_0

gem 'rails', '4.0.2'

group :development, :test do
  .
  .
  .
  gem 'spork-rails', '4.0.0'
  gem 'guard-spork', '1.5.0'
  gem 'childprocess', '0.3.6'
end
.
.
.
Листинг 3.36. Gemfile для примера приложения.

Затем установите Spork используя bundle install:

$ bundle install

Затем сделайте начальную загрузку конфигурации Spork:

$ bundle exec spork --bootstrap

Теперь нам необходимо отредактировать конфигурационный файл RSpec spec/spec_helper.rb таким образом, чтобы окружение загружалось в блоке prefork который обеспечит его однократную загрузку (Листинга 3.37).

require 'rubygems'
require 'spork'

Spork.prefork do
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'rspec/autorun'

  # Requires supporting ruby files with custom matchers and macros, etc,
  # in spec/support/ and its subdirectories.
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  # Checks for pending migrations before tests are run.
  # If you are not using ActiveRecord, you can remove this line.
  ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)

  RSpec.configure do |config|
    # ## Mock Framework
    #
    # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
    #
    # config.mock_with :mocha
    # config.mock_with :flexmock
    # config.mock_with :rr

    # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
    config.fixture_path = "#{::Rails.root}/spec/fixtures"

    # If you're not using ActiveRecord, or you'd prefer not to run each of your
    # examples within a transaction, remove the following line or assign false
    # instead of true.
    config.use_transactional_fixtures = true

    # If true, the base class of anonymous controllers will be inferred
    # automatically. This will be the default behavior in future versions of
    # rspec-rails.
    config.infer_base_class_for_anonymous_controllers = false

    # Run specs in random order to surface order dependencies. If you find an
    # order dependency and want to debug it, you can fix the order by providing
    # the seed, which is printed after each run.
    #     --seed 1234
    config.order = "random"
    config.include Capybara::DSL
  end
end

Spork.each_run do
  # This code will be run each time you run your specs.

end
Листинг 3.37. Добавление загрузки окружения в блок Spork.prefork. spec/spec_helper.rb

Перед запуском Spork мы можем сделать контрольный замер времени необходимого на прохождения тестов следующим образом:

$ time bundle exec rspec spec/requests/static_pages_spec.rb
......

6 examples, 0 failures

real 0m8.633s
user 0m7.240s
sys  0m1.068s

Здесь набор тестов занимает более семи секунд, даже при том, что фактически тесты отрабатывают менее чем за одну десятую секунды. Чтобы ускорить это мы можем открыть отдельное окно терминала, переместиться в корневой Rails каталог, а затем запустить сервер Spork:

$ bundle exec spork
Using RSpec
Loading Spork.prefork block...
Spork is ready and listening on 8989!

(Для того чтобы избавиться от необходимости запускать команду с префиксом bundle exec, повторите шаги из Раздела 3.6.1.) В другом терминальном окне, мы теперь можем запустить наш набор тестов с --drb опцией ("распределенный Ruby") и проверить что издержки, связанные с постоянной перезагрузкой окружения значительно сократились:

$ time bundle exec rspec spec/requests/static_pages_spec.rb --drb
......

6 examples, 0 failures

real 0m2.649s
user 0m1.259s
sys  0m0.258s

Включать опцию --drb при каждом запуске rspec довольно неудобно, поэтому я рекомендую добавить ее в .rspec файл расположенный в корневой директории проекта, как это показано в Листинге 3.38.

--colour
--drb
Листинг 3.38. Конфигурирование Rspec для автоматического использования Spork. .rspec

Небольшой совет касательно использования Spork: если ваши тесты провальны, а вы думаете, что они должны проходить, проблема может быть связана со Spork, который иногда может не перегружать необходимые файлы. (Например, это часто происходит при изменении конфигурационных файлов, таких как routes.rb.) При возникновении подобных сомнений, отключите Spork сервер командой Ctrl-C и рестартуйте его:

$ bundle exec spork
Using RSpec
Loading Spork.prefork block...
Spork is ready and listening on 8989!
^C
$ bundle exec spork

Guard и Spork

Спорк особенно полезен в комбинации с Guard, мы можем подружить их следующим образом:

$ bundle exec guard init spork

Затем нам необходимо изменить Guardfile как в Листинге 3.39.

require 'active_support/inflector'

guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' },
               :rspec_env    => { 'RAILS_ENV' => 'test' } do
  watch('config/application.rb')
  watch('config/environment.rb')
  watch('config/environments/test.rb')
  watch(%r{^config/initializers/.+\.rb$})
  watch('Gemfile')
  watch('Gemfile.lock')
  watch('spec/spec_helper.rb') { :rspec }
  watch('test/test_helper.rb') { :test_unit }
  watch(%r{features/support/}) { :cucumber }
end

guard 'rspec', all_after_pass: false, cli: '--drb' do
  .
  .
  .
end
Листинг 3.39. Guardfile обновленный для использования Spork. Guardfile

Обратите внимание что мы обновили аргументы guard включив cli: '--drb', которая обеспечивает использование Guard-ом интерфейса командной строки (cli) для Spork сервера. Мы также добавили команду для слежения за директорией features/support/, которую мы начнем менять начиная с Главы 5.

Закончив с конфигурацией, мы можем одновременно стартовать Guard и Spork командой guard:

$ bundle exec guard

Guard автоматически стартует Spork сервер, кардинально уменьшая издержки при запуске тестов.

Хорошо сконфигурированное тестовое окружение с Guard, Spork и (опционально) уведомлениями вызывают привыкание к разработке через тестирование. См. более подробно об этом в скринкастах Rails Tutorial14http://railstutorial.org/screencasts.

Запускаем тесты внутри Sublime Text

Если вы используете Sublime Text, то у вас есть большое количество вспомогательных команд для запуска тестов непосредственно в редакторе. Для того чтобы научиться пользоваться ими, следуйте инструкциям для своей платформы на Sublime Text 2 Ruby Tests.15https://github.com/maltize/sublime-text-2-ruby-tests На моей платформе (Macintosh OS X), я могу установить команды следующим образом:

$ cd ~/Library/Application\ Support/Sublime\ Text\ 2/Packages
$ git clone https://github.com/maltize/sublime-text-2-ruby-tests.git RubyTest

Вы также можете пока использовать инструкции по настройке для Rails Tutorial Sublime Text.16https://github.com/mhartl/rails_tutorial_sublime_text

После перезапуска Sublime Text, пакет RubyTest предоставляет следующие команды:

  • Command-Shift-R:запуск одного теста (если запускается на it блоке) или группы тестов (если выполняется на describe блоке)
  • Command-Shift-E:запуск последнего теста(-ов)
  • Command-Shift-T:запуск всех тестов в текущем файле

Поскольку набор тестов может стать довольно медленным даже для относительно небольших проектов, возможность запускать один тест (или небольшую группу тестов) за раз может сильно упростить жизнь разработчика. Даже один единственный тест требует загрузки Rails окружения, вот почему эти команды отлично работают вместе со Spork: запуск одного теста устраняет необходимость выполнения всего файла с тестами, в то время как Spork устраняет издержки связанные с рестартом тестового окружения. Вот последовательность команд которую я рекомендую:

  1. Стартовать Spork в терминале.
  2. Написать один тест или небольшую группу тестов.
  3. Выполнить Command-Shift-R чтобы убедиться что тест или группа тестов в красном.
  4. Написать соответствующий код приложения.
  5. Выполнить Command-Shift-E для повторного запуска того же теста/группы тестов для проверки того что они позеленели.
  6. Повторить шаги 2 – 5 при необходимости.
  7. По достижении естественной точки остановки (например, перед коммитом), запустить rspec spec/ в командной строке для того, чтобы убедиться что полный набор тестов по-прежнему в зеленом.

Даже с возможностью запуска тестов внутри Sublime Text, я по-прежнему предпочитаю использовать Guard, но в настоящий момент меня кормит TDD техника описанная выше.

Вадим Обозин
Вадим Обозин

Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора?