Rails Note #15: Rails 3, Devise, Cucumber
1. Installed rvm (Ruby Version Manager). See Episode 200. Rails3 Beta and RVM
rupert:tsa rupert$ rvm ruby-1.9.2-p0 rupert:tsa rupert$ ruby -v ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.5.0]
To use ruby-1.9.2-p0 as default
rupert:rails3 rupert$ rvm ruby-1.9.2-p0 --default rupert:rails3 rupert$ ruby -v ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.5.0]
2. Install rails3
gem install rails3. Create a rails app tsa (the sample app)
rails new tsa -d mysql4. Start webrick. Yup, script/server is gone
rupert:tsa rupert$ rails server -e development => Booting WEBrick => Rails 3.0.3 application starting in production on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2010-12-06 20:47:27] INFO WEBrick 1.3.1 [2010-12-06 20:47:27] INFO ruby 1.9.2 (2010-08-18) [x86_64-darwin10.5.0] [2010-12-06 20:47:27] INFO WEBrick::HTTPServer#start: pid=1348 port=3000
Note: Upon checking http://127.0.0.1:3000/ I get a “Routing Error”
5. Update your GemFile with the necessary gems.
Let’s use devise as a login authentication system.
source 'http://rubygems.org' gem 'rails', '3.0.3' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' gem 'mysql2' gem 'devise', :git => "git://github.com/plataformatec/devise.git", :branch => "master"
Then run bundle install. This will install devise gem.
rupert:tsa rupert$ bundle install Fetching git://github.com/plataformatec/devise.git remote: Counting objects: 10326, done. remote: Compressing objects: 100% (3745/3745), done. remote: Total 10326 (delta 6603), reused 9755 (delta 6163) Receiving objects: 100% (10326/10326), 1.24 MiB | 314 KiB/s, done. Resolving deltas: 100% (6603/6603), done. Fetching source index for http://rubygems.org/
Where are the gems installed?
rupert:tsa rupert$ bundle show devise /Users/rupert/.rvm/gems/ruby-1.9.2-p0/bundler/gems/devise-b50fd1a72e71
6. Create the necessary databases in mysql. See config/database.yml
CREATE DATABASE tsa_development;
7. Run devise generator.
rupert:tsa rupert$ rails generate devise:install create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Some setup you must do manually if you havent yet: 1. Setup default url options for your specific environment. Here is an example of development environment: config.action_mailer.default_url_options = { :host => 'localhost:3000' } This is a required Rails configuration. In production it must be the actual host of your application 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root :to => "home#index" 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> ===============================================================================
8. Generate a controller in Rails3.
rupert:tsa rupert$ rails generate controller main index
create app/controllers/main_controller.rb
route get "main/index"
invoke erb
create app/views/main
create app/views/main/index.html.erb
invoke test_unit
create test/functional/main_controller_test.rb
invoke helper
create app/helpers/main_helper.rb
invoke test_unit
create test/unit/helpers/main_helper_test.rbUpdate routes.rb for the root_url
root :to => "main#index"
9. Create a User model with devise.
rupert:tsa rupert$ rails generate devise User
invoke active_record
create app/models/user.rb
invoke test_unit
create test/unit/user_test.rb
create test/fixtures/users.yml
create db/migrate/20101206104321_devise_create_users.rb
insert app/models/user.rb
route devise_for :users
rupert:tsa rupert$10. Run migration for User model
rupert:tsa rupert$ export RAILS_ENV=development rupert:tsa rupert$ rake db:migrate (in /Users/rupert/projects/rails3/tsa) == DeviseCreateUsers: migrating ============================================== -- create_table(:users) -> 0.0805s -- add_index(:users, :email, {:unique=>true}) -> 0.1368s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.1484s == DeviseCreateUsers: migrated (0.3662s) =====================================
11. Using cucumber
sudo port install libxml2 libxslt
12. Update Gemfile
group :test, :development do gem 'rspec' gem 'rspec-rails' gem 'cucumber' gem 'cucumber-rails' gem 'capybara' gem 'launchy' end
13. Rspec
rupert:tsa rupert$ rails g rspec:install create .rspec create spec create spec/spec_helper.rb create autotest create autotest/discover.rb
14. Cucumber
rupert:tsa rupert$ rails g cucumber:install --rspec --capybara create config/cucumber.yml create script/cucumber chmod script/cucumber create features/step_definitions create features/step_definitions/web_steps.rb create features/support create features/support/paths.rb create features/support/env.rb exist lib/tasks create lib/tasks/cucumber.rake gsub config/database.yml gsub config/database.yml force config/database.yml
15. Write your first feature
Feature: Home Page In order to make sure the home page works As a normal user I want to see the home page Scenario: Show Main Sections When I go to the home page Then show me the page Then I should see "Login"
From Harold Jimenez:
“The Given step is where you set up the context of your scenario. Every scenario starts with a blank slate, so it is important to create a state in your application for example by creating data in the database, or by navigating to a specific page.
The When step is where you exercise the application in order to accomplish what needs testing. In the case of a web app like twiddr, this is usually where you fill in forms, press buttons, click links, or otherwise interact with the system in some way.
Finally, the Then step is where you verify the result, and it’s where we check that the correct pages are rendered, that we see a success or error message, or anything that could help us verify that the prior action was successful. As we move along with creating our own features, this will become much clearer.”
16. See it fail in cucumber
rake features (or) cucumber (or) bundle exec cucumber features/home.feature
rupert:tsa rupert$ cucumber Using the default profile... F-- (::) failed steps (::) Can't find mapping from "the home page" to a path. Now, go and add a mapping in /Users/rupert/projects/rails3/tsa/tsa/features/support/paths.rb (RuntimeError) ./features/support/paths.rb:26:in `rescue in path_to' ./features/support/paths.rb:20:in `path_to' ./features/step_definitions/web_steps.rb:24:in `/^(?:|I )go to (.+)$/' features/home.feature:7:in `When I go to the home page' Failing Scenarios: cucumber features/home.feature:6 # Scenario: Show Main Sections 1 scenario (1 failed) 3 steps (1 failed, 2 skipped) 0m0.062s
17. Defining “the home page” in the support/paths.rb
when /the home page/ root_path
and in the routes.rb
root :to => "home#index"
Other examples:
when /the profile page for "([^\"]+)"/ user = User.find_by_twiddr_name!($1) user_path(user)
Note: When you see the error
undefined local variable or method `node' for #<Capybara::Driver::RackTest::Node:0x00000101151ad8> (NameError)"
You need to comment in features/support/env.rb
#require 'cucumber/rails/capybara_javascript_emulation'For more info, read https://rspec.lighthouseapp.com/projects/16211-cucumber/tickets/674
18. Then show me the page will open up the browser
file://localhost/Users/rupert/projects/rails3/tsa/tsa/tmp/capybara/capybara-20101209222149.html
19. Automatic Testing with “autotest”. When you save a file, autotest automatically runs cucumber. To avoid autotest infinite loop with cucumber, make sure your IDE (i.e rubymine) does not save automatically in the file system. You can exclude files and directories from autotest with a “.autotest” file
Autotest.add_hook :initialize do |at| at.add_exception(%r{^\./\.git}) at.add_exception(%r{^\./db}) at.add_exception(%r{^\./log}) at.add_exception(%r{^\./tmp}) at.add_exception(%r{^\./.idea}) at.add_exception(%r{^\./rerun\.txt}) at.add_exception(%r{^\./Gemfile\.lock}) end
Now you can run, autotest from the terminal. Save a file and see autotest run.
autotest
20. If you have javascript AJAX calls included in cucumber scenarios, you need to append them with @javascript tag. Below is a sample which selects “Australia” from the “Country” and waits for the ajax request to finish before selecting the “State” drop down. More info on capybara github.
@javascript Scenario: Creating a Fleet And I select "Australia" from "Country" And I wait for the ajax request to finish And I select "Victoria" from "State"
How does the step “I wait for the ajax request to finish” looks like? http://stackoverflow.com/questions/7286254/cucumber-wait-for-ajaxsuccess
When /^I wait for the ajax request to finish$/ do start_time = Time.now page.evaluate_script('jQuery.isReady&&jQuery.active==0').class.should_not eql(String) until page.evaluate_script('jQuery.isReady&&jQuery.active==0') or (start_time + 5.seconds) < Time.now sleep 1 end end
