Rails, state_machine, Rspec, capybara, factory_girl, spork и другое. Часть III.

В предыдущих частях мы закончили настраивать наше тестовое окружение. Теперь с чистой, так сказать, совестью можем приступать к написанию самих тестов и дальнейшему развитию приложения.

22. И начнём с «фабричных девчёнок: factory_girl. Этот замечательный gem необходим для того, чтобы упростить создание примеров объектов, которые используются в тестах. Например, чтобы каждый раз не создавать и не повторяться в разных тестах при создании нового пользователя, можно отлично использовать factory_girl. Итак, создадим файл spec/factories.rb для начала следующего содержания:

FactoryGirl.define do 

factory :user do

name ‘none’

email ‘none@example.com’

password ’1111111111′

end 

end

Здесь мы задали параметры для вновь создаваемого пользователя. Не забудьте добавить в spec/spec_helper.rb:

require ‘factory_girl’

23. Теперь открываем файл spec/models/user_spec.rb. Сейчас он должен быть практически пустым:

require ‘spec_helper’

 describe User do

 end

Теперь вставим следующий фрагмент кода сразу же за директивой  describe User do:

before(:each) do

user = Factory(:user)

end

Данный блок, как следует из его названия и определения, перед (before) каждым (each) тестом создавать нам пользователя, которого мы можем использовать в тестах.

24. Давайте напишем первые наши тесты. Сразу же за блоком before пишем следующее:

it ‘should create a new user with state new’ do

newuser = User.new(@user)

newuser.new?.should be_true

end

 it ‘should create a new user with state not eq active’ do

newuser = User.new(@user)

newuser.active?.should be_false

end

 it ‘should create a new user with state not eq blocked’ do

newuser = User.new(@user)

newuser.blocked?.should be_false

end

Все тесты имеют одинаковую структуру:

it ‘ожидание от теста’ do

выполнение каких-либо действий, проверок

end

Первый наш тест создаёт нового пользователя и проверяет его состояние — оно должно быть new:

newuser.new?.should be_true

Не удивляйтесь и не пугайтесь, если эта запись немного не понятна. Что вообще за «new?» и откуда оно взялось? Всё по порядку, позже объясню. Эту запись можно переписать другим способом, даже более понятным образом:

newuser.state.should eq(«new»)

Это как-то больше похоже на человеческий язык, правда, английский: «статус созданного пользователя должен быть эквивалентен новому».  Для задания и изменения статуса мы будем использовать замечательный gem state_machine.  Если кто-нибудь из читателей знаком с понятием «конечные автоматы», то ничего нового он не узнает. Для тех же, кто не в курсе, то state_machine — это способ описания состояний автомата, в нашем случае — это записи в таблице User, а так же методы перехода из одного состояния в другое.

Так как это не настоящее приложение, а так, для потренироваться, то я предположил, что Пользователи будут иметь всего три состояния:

1. new — это только что созданный пользователь

2. active — это пользователь, который активированный и может выполнять какие-либо действия

3. blocked — это заблокированный пользователь, соответственно, он ничего не может сделать

Это, иными словами, описание состояний автомата. Чуть позже опишу о способах изменения состояния, а пока обращаю Ваше внимание, что приведённые выше тесты не проходят, да… Что ж, давайте мелкими шажками делать, чтоб эти тесты проходили (-:

25. Описание state_machine очень неплохо документировано, настоятельно рекомендую ознакомиться с содержимым сайта. Я же предлагаю открыть открыть файл нашей модели app/models/user.rb и вписать туда следующее:

state_machine :initial => :new do

end

Этим самым мы описываем начальное состояние нашего конечного автомата. По умолчанию state_machine как раз и использует поле state, которое мы указали при генерации модели. Как видно из приведённой выше записи, начальное состояние будет «new». Но это ещё не всё (-: Кроме задания первоначального состояния в «new», state_machine даёт нам в помощь некоторые вспомогательные методы, которые дают нам возможность проверить состояние автомата. Один из них «new?», который возвратит «истина», если, как говорят у нас в Одессе, «состояние таки да new», и «ложь», если какое-то другое. Именно этот метод мы использовали в выражении «newuser.new?.should be_true«: а правда ли, что состояние нового пользователя есть «new»? (-: Ну что-то типа того.

26. После сохранения внесённых изменений, первый наш тест должен проходить! Ура, товарищи! (((-: Но только один из трёх… на оставшиеся должно выдавать ошибку, что-то типа этой:

1) User should create a new user with state not eq active

Failure/Error: newuser.active?.should be_false

NoMethodError:

undefined method `active?’ for #<User:0xb09a500>

# ./spec/models/user_spec.rb:23:in `block (2 levels) in <top (required)>’

Надо с этим что-то делать (-: А сделать надо простою вещь: добавить в наш файл модели две строчки

state :active

state :blocked

Иными словами, описать ещё два доступных состояния, которые мы пытаемся проверить во 2м и 3м тестах. После сохранения файла модели, все тесты должны пройти успешно!

В следующий раз я продолжу написание тестов и дальнейшее развитие нашего проекта.

Сентябрь 29th, 2011 by none | 2 комментария

Rails, state_machine, Rspec, capybara, factory_girl, spork и другое. Часть II.

В предыдущей части мы установили и настроили Rspec, guard и т.п.В этой части мы продолжим настраивать наше тестовое окружение.

11. В принципе, мы уже можем переключиться на написание тестов, но мы не ищем лёгких путей! (-: Как бы оно там ни было, но с возрастанием количества тестов растёт время, затраченное на их прохождение. Чтобы хоть как-то сократить его, существует чудесное решение — spork. Spork представляет из себя DRb сервер для различных тестировочных фреймворков. В Gemfole он у нас прописан, приступим.  Настроим spork:

$ bundle exec spork —bootstrap 

Using RSpec

Bootstrapping /home/none/rails/state_machine31/spec/spec_helper.rb.

Done. Edit /home/none/rails/state_machine31/spec/spec_helper.rb now with your favorite text editor and follow the instructions.

12. Последуем рекомендациям и внесём изменения в spec/spec_helper.rb. Он должен принять примерно следующий вид:

Spork.prefork do

# Loading more in this block will cause your tests to run faster. However,

# if you change any configuration or code from libraries loaded here, you’ll

# need to restart spork for it take effect.

ENV["RAILS_ENV"] ||= ‘test’

require File.expand_path(«../../config/environment», __FILE__)

require ‘rspec/rails’

# Requires supporting files with custom matchers and macros, etc,

# in ./support/ and its subdirectories.

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

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

config.mock_with :rspec

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, comment the following line or assign false

# instead of true.

config.use_transactional_fixtures = true

end

end

Как видите, основные изменения коснулись секции Spork.prefork.

13. Запускаем spork:

$ spork

Using RSpec

Preloading Rails environment

Loading Spork.prefork block…

Spork is ready and listening on 8989!

14. Тесты будут проходить уже значительно быстрее. Но это ещё тоже не всё. Мы же уже используем утилиту guard. Надо бы их обоих как-то подружить! Для этого в Gemfile добавляем:

gem ‘guard-spork’

И  сразу же:

$ bundle install

15. Теперь укажем guard-у, что надо бы работать со spork:

$ guard init spork

spork guard added to Guardfile, feel free to edit it

16. А вот теперь надо немного подшаманить Guardfile. У меня он в итоге получился следующего содержания:

# A sample Guardfile

# More info at https://github.com/guard/guard#readme

 

guard ‘spork’, :wait => 60, :cucumber => false, :test_unit => false, :rspec_env => { ‘RAILS_ENV’ => ‘test’ } do

watch(‘config/application.rb’)

watch(‘config/environment.rb’)

watch(%r{^config/environments/.+\.rb$})

watch(%r{^config/initializers/.+\.rb$})

watch(‘spec/spec_helper.rb’)

end

 

guard ‘rspec’, :cli => «—drb», :version => 2 do

watch(%r{^spec/.+_spec\.rb$})

watch(%r{^lib/(.+)\.rb$}) { |m| «spec/lib/#{m[1]}_spec.rb» }

watch(‘spec/spec_helper.rb’) { «spec/» }

 

# Rails example

watch(%r{^spec/.+_spec\.rb$})

watch(%r{^app/(.+)\.rb$}) { |m| «spec/#{m[1]}_spec.rb» }

watch(%r{^lib/(.+)\.rb$}) { |m| «spec/lib/#{m[1]}_spec.rb» }

watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb», «spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb», «spec/acceptance/#{m[1]}_spec.rb»] }

watch(%r{^spec/support/(.+)\.rb$}) { «spec/» }

watch(‘spec/spec_helper.rb’) { «spec/» }

watch(‘config/routes.rb’) { «spec/routing» }

watch(‘app/controllers/application_controller.rb’) { «spec/controllers» }

# Capybara request specs

watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| «spec/requests/#{m[1]}_spec.rb» }

end

Обращаю внимание на то, что секция spork должна быть раньше, чем секция rspec. Иначе всё будет не правильно работать. Как можете заметить, я убрал запуск spork-а для «огурца» и тест_юнита. А вообще, буквально недавно появился отличный скринкаст от Ryan Bates о spork. Ну, естественно, для знающих английский.

17. От теперь можно опять запускать guard:

$ guard start

Лично у меня выдаёт огромное количество ошибок, типа:

1) UsersController GET index assigns all users as @users

Failure/Error: user = User.create! valid_attributes

ActiveRecord::StatementInvalid:

Could not find table ‘users’

# ./spec/controllers/users_controller_spec.rb:32:in `block (3 levels) in <top (required)>’

Ну нет таблицы ‘user’ и хоть ты тресни (-:

18. Придётся уже создать наконец-то эту таблицу:

$ rake db:migrate

19. Однако тесты всё так же не проходят… беда-беда, огорчение! (-: Дело всё в том, что для тестов нужна своя база, свои таблицы. Кто не знает, то разработка осуществляется в среде development, а тестирование — в среде test. Поэтому нам надо сделать среду test идентичной среде development. Для этого выполним:

$ rake db:test:prepare

20. Теперь все тесты, сгенерированные scaffold, должны проходить. У меня пишет следующее:

Finished in 1.93 seconds

30 examples, 0 failures, 2 pending

Done.

21. Теперь можно приступать к написанию свои тестов и дальнейшего развития приложения. Но об этом уже в следующей части.

Сентябрь 28th, 2011 by none | Комментариев нет

Rails, state_machine, Rspec, capybara, factory_girl, spork и другое. Часть I.

Решил я, так сказать, ломать свой стереотип разработки. В жизни никогда не писал тестов. А всё прогрессивное человечество пишет. Даже понапридумывало всякие сокращения: TDD, BDD и т.п. Я не собираюсь относить к какой-то категории всё то, что напишу в данном цикле статей. Я просто буду учиться писать тесты. Может кто-то будет учиться вместе со мной (-:

Статей будет несколько. Так что любой желающий сможет всё написанное мной повторить у себя на машине. В этой части я опишу, как настроить среду для запуска тестов, как создать каркас нашего будущего приложения. Ну не буду толочь воду в ступе, приступим.

1. Создадим новый проект:

$ rails new state_machine31
Я его так назвал, так как хотел поиграться со state_machine и на Rails 3.1. Так что вот такое не замысловатое название проекта (-:
$ cd ./state_machine31
2. Вносим изменения в наш Gemfile:

gem ‘state_machine’

gem ‘ruby-graphviz’, :require => ‘graphviz’ # Optional: only required for graphing
gem ‘therubyracer’
gem «rspec-rails», :group => [:test, :development]
group :test do

# Pretty printed test output
gem ‘turn’, :require => false
gem «factory_girl_rails»
gem «capybara»
gem «guard-rspec»
gem «spork»
end
3. Неизменное, после изменения Gemfile:
$ bundle install
4. Запускаем установку Rspec:

$ rails g rspec:install
create  .rspec
create  spec
create  spec/spec_helper.rb

5. В spec_helper.rb вносим такие изменения, всё исключительно на основании рекомендаций разработчиков:
добавляем: require ‘capybara/rspec’
убираем: config.fixture_path = «#{::Rails.root}/spec/fixtures»

6. Теперь давайте сделаем каркас нашего приложения, с которым и будем работать:

$ rails generate scaffold User name:string email:string password:string state:string

В данном случае просто создаётся всё необходимое, для управление простенькой базой пользователей. Информация о пользователе будет состоять из его имени (name), адреса электронной почты (email), пароля(password) и поля состояния(state). Последнее поле state нам необходимо для работы со state_machine, для элементарных функций. У пользователя может быть только 3 состояния: только что созданный(new), активированный (active) и заблокированный(blocked).

7. Самое время запустить настройку guard. Эта консольная утилита следит за изменениями в наших файлах и автоматически запускает соответствующие тесты. Вы полюбите её (-:

$ guard init rspec

Будет создан файл Guardfile в корневой директории нашего приложения.

8. Запускаем первый раз наш guard:

$ guard

Please install rb-inotify gem for Linux inotify support

Using polling (Please help us to support your system better than that.)

Guard is now watching at ‘/home/none/rails/state_machine31′

Guard::RSpec is running, with RSpec 2!

Running all specs

Не тут-то было. Никаких тебе всплывающих подсказок, только куча провальных тестов (-:

9. Добавляем в Gemfile

gem ‘rb-inotify’

gem ‘libnotify’

Затем опять:

$ bundle install

10. Опять запускаем guard, опять видим кучу провальных тестов, но уже с левой стороны всплывающие окошки с указанием, сколько тестов вообще, сколько провальных, сколько понадобилось времени и всё такое. Красота!

В следующий раз я расскажу, как настроить spork, factory_girl и т.п.

Если всё написать за раз — это будет слишком длинное полотно, никто не осилит (-:

 

Сентябрь 27th, 2011 by none | Комментариев нет

Обновления gem-ов в системе

Как обновить все установленные gem-ы в системе? Да лекго и просто! Выполните следующую команду да и все дела:

$ gem update —system

Успехов в программировании и всех с праздником — Днём программиста! (-:

Сентябрь 13th, 2011 by none | 2 комментария

annotate в rails 3.1

Как помните, решил я тут побаловаться «рельсами» 3.1. И есть такой замечательный гем — annotate. Весь смысл в том, что он на основании информации из БД вставляет в файл модели информацию о полях соответствующей таблицы. Иногда это бывает это очень удобно, ведь запомнить все поля бывает иногда проблематично.

Так вот. Не запустился! (-: Грязно выругался при запуске и не запустился:

$ annotate
/home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activerecord-3.1.0/lib/active_record/railties/databases.rake:3:in `<top (required)>’: undefined method `namespace’ for main:Object (NoMethodError)
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:234:in `load’
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:234:in `block in load’
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:223:in `block in load_dependency’
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:640:in `new_constants_in’
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:223:in `load_dependency’
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:234:in `load’
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/activerecord-3.1.0/lib/active_record/railtie.rb:26:in `block in <class:Railtie>’
from /home/none/.rvm/gems/ruby-1.9.2-p290@myproject/gems/railties-3.1.0/lib/rails/railtie.rb:183:in `call’

Ну и т.п. Надо что-то делать…. а делать надо следующее.

1. Добавляем в Gemfile:

gem ‘annotate’, :git => ‘git://github.com/jeremyolliver/annotate_models.git’, :branch => ‘rake_compatibility’

2. Теперь выполняем, как обычно:

$ bundle install

3. А теперь несколько иначе, чем раньше:

$ bundle exec annotate

Вот теперь всё должно получиться. (-:

Сентябрь 10th, 2011 by none | Один комментарий