You are currently browsing the tag archive for the 'RSpec' tag.

On my new project we had the problem that some specs failed when ran on their own, and some specs produces strange output (like “use object_id”), so I build a helper that runs each test on its own and could pinpoint the problem.

rake spec:one_by_one

it is now included in the single test rails plugin

What good is a dual-core when you do not use it !?

rake spec:parallel[1] –> 86 seconds
rake spec:parallel[2] –> 47 seconds
rake spec:parallel[4] –> 26 seconds

grab the parallel specs rails plugin here!

It requires a bit of setup, but hey: Can you spare 5 minutes now to save 1 minute 100 times a day ?

You just have to love this “syntax” :D
Story

#features/discussion.feature
Scenario: I add to an discussion
    Given I am logged in
    And a "Discussion" exists for "Festival" "1"
    And I am on "Festival" "1"
    When I fill in "Text" with "Halloo"
    And I press "Send"
    Then I should be on "Festival" "1"
    And I should see "Halloo"

Steps

#features/step_definitions/common_steps.rb
# On page/record
Given /^I am on "([^"]*)"$/ do |path|
  visit path
end

Then /^I should be on "([^"]*)"$/ do |path|
  current_path.should == path
end

Given /^I am on "([^"]*)" "([^"]*)"$/ do |model,number|
  visit polymorphic_path(record_from_strings(model,number))
end

Then /^I should be on "([^"]*)" "([^"]*)"$/ do |model,number|
   current_path.should == polymorphic_path(record_from_strings(model,number))
end

# Existing
Given /^a "([^"]*)" exists for "([^"]*)" "([^"]*)"$/ do |associated,model,number|
  record = record_from_strings(model,number)
  record.send(associated.underscore+'=',valid(associated))
  record.save!
end

# Login
Then /^I should be logged in$/ do
  should be_logged_in
end

Given /^I am logged in$/ do
  visit 'login'
  fill_in 'email', :with=>'quentin@example.com'
  fill_in 'password', :with=>'test'
  click_button 'Login'
end

# Support
def current_path
  response.request.request_uri
end

def record_from_strings(model,number)
  model.constantize.find(:first,:offset=>number.to_i-1)
end

env.rb

#features/support/env.rb
#load all fixtures
include AuthenticatedTestHelper #restful_authentification
include ValidAttributes #http://github.com/grosser/valid_attributes

I finally found some time to mess with cucumber, its pretty nice but has some edges and quircks… but for now i like it :)

Install
This was rather hard… (these libraries and frex should not be necessary for mac)

sudo apt-get install libxslt1-dev libxml2-dev racc
sudo gem install aaronp-frex brynary-webrat
sudo gem install cucumber
./script/generate cucumber
rake features

First story

#features/signup.feature
Feature: Signup
  Scenario: A new user comes to the page and wants to register.
    Given I am on "/"
    When I click "register"
    And I fill in "user[name]" with "Michael G"
    And I fill in "Email" with "test@test.de"
    And I fill in "Password" with "test"
    And I fill in "Password confirmation" with "test"
    And I press "Register for the free basic account"
    Then I should be on "/"
    And I should be logged in
    And I should see a flash about "activation"

Environment setup

#append to features/support/env.rb
#load all fixtures -- http://pragmatig.wordpress.com/2008/12/25/load-all-fixtures-when-not-in-test/
include AuthenticatedTestHelper # restful authentification
include RspecResponseEnhancer # http://pragmatig.wordpress.com/2008/04/20/rspec-responseshould-information-enhancers/

Common steps
There is a nice collection of steps already available in steps/webrat_steps, but some basics where missing….

#features/step_definitions/common_steps.rb
When /^I click "(.*)"$/ do |link|
  click_link(link)
end

When /^I am on "(.*)"$/ do |path|
  header('Accept-Language','EN-en')
  visit path
end

Then /^I should be on "(.*)"$/ do |path|
  response.request.request_uri.should == path
end

Then /^I should see a flash about "(.*)"/ do |message|
  doc.search('div.flash_notice').inner_html.include?(message).should be_true
end

Then /^I should be logged in$/ do
  should be_logged_in
end

def doc
  Hpricot(response.body)
end

Output

 rake features
Feature: Signup  # features/signup.feature
  Scenario: A new user comes to the page and wants to register.  # features/signup.feature:2
    Given I am on "/"                                            # features/step_definitions/common_steps.rb:6
    When I click register                                        # features/step_definitions/common_steps.rb:1
    And I fill in "user[name]" with "Michael G"            # features/step_definitions/webrat_steps.rb:12
    And I fill in "Email" with "test@test.de"                    # features/step_definitions/webrat_steps.rb:12
    And I fill in "Password" with "test"                         # features/step_definitions/webrat_steps.rb:12
    And I fill in "Password confirmation" with "test"            # features/step_definitions/webrat_steps.rb:12
    And I press "Register for the free basic account"            # features/step_definitions/webrat_steps.rb:4
    Then I should be on "/"                                      # features/step_definitions/common_steps.rb:11
    And I should be logged in                                    # features/step_definitions/common_steps.rb:19
    And I should see a flash about "activation"                  # features/step_definitions/common_steps.rb:15

10 steps passed

Feels great to write stories again, sadly takes more time then unit tests, but the readability is superb! IMO a very nice way of doing integration tests…

snow tree by plain ethos

TDD often is well understood, but seldom put to good use. Spikes grow larger, hard to test aspects are skipped and sooner or later your test coverage looks like this.

.

Therefore i want to show you the Black-White-Tree testing method, which is easy to adopt and results in full C1(path) coverage with easy to maintain, independent tests.

.

.

ice tree by serendipitypeace2007

ice tree by serendipitypeace2007

The principle is simple:

When designing a new method build Black-Box tests for it, often 2-3 are sufficient if they exersice all paths within this method(not necessarily its sub-methods), represented by the trunk and the black branches.

describe :price do
  it "sums prices and applies discounts" do
    Order.new(:items=>items,:discount=>20).price.should == 22.5
  end
  it "costs nothing if it is free" do
    Order.new(:items=>items,:free=>true,:discount=>10).price.should == 0
  end
end

Then write White Box tests, for the public method, mocking everything out with forged return values to verify that every method is called and the call-results are used logically.

describe :price do
  ...
  it "uses sum_price and apply_discount" do
    order = Order.new(:items=>items,:discount=>10)
    order.expects(:sum_prices).returns 100
    order.expects(:apply_discount).with(10,100).returns 20
    order.price.should == 20
  end
end

Then build the method, making all White Box and some of the Black Box tests pass.
Repeat for every sub-method.