Sonntag, 11. Mai 2014

RSpec On Rails

In the previous post Do it RSpec the usage of RSpec in a Ruby project was described generally. This post explains how to code a simple RSpec test in a Ruby on Rails project.
The installation of RSpec itself is alike:
user$ gem install rspec
In a Rails project the rspec-rails gem has to be added to the Gemfile:
gem "rspec-rails", :group => [:test, :development]
Runing the Bundler:
user$ bundle
and preparing the Rails project for RSpec tests:
user$ rails g rspec:install
It generates the subfolder spec and a spec helper within. The spec helper file contains logic considering all tests. At this point only testing models is covered. That's why creating the folder:
user$ mkdir spec/models
Generating the model:
user$ rails g model Person name:string surname:string birthday:date active:boolean
The generated models/person.rb looks blank like:
class Person < ActiveRecord::Base
  attr_accessible :birthday, :name, :surname, :active
end
Some tests require test objects, called fixtures. Creating the folder for them:
user$ mkdir spec/fixtures
Fixtures are defined in a YAML notation, e.g.:
david:
id: 1 
name: David
surname: Heinemeier Hansson 
birthday: 1979-10-15
Saving this single fixture in the file spec/fixtures/people.yml. Please keep in mind, the more fixtures you create for several tests the harder to maintain changes of fixtures.
Creating the spec/models/person_spec.rb:
require 'spec_helper'

describe Person do
  fixtures :people
    
  before(:each) do
    @person = people(:david)
  end

  it "should not be valid without a name" do
    @person.name = ''
    @person.should_not be_valid
  end

  it "should be activated before validation" do
    @person.create.should be_active
  end
end
That are pretty little specifications. Before fullfilling them, the test was run:
user$ rspec spec/models/person_spec.rb
it returns:
Failures:

  1) Person should not be valid without a name
     Failure/Error: @person.should_not be_valid
       expected valid? to return false, got true
     # ./spec/models/person_spec.rb:12:in `block (2 levels) in '

  2) Person should be activated before validation
     Failure/Error: @person.should be_active
       expected active? to return true, got false
     # ./spec/models/person_spec.rb:17:in `block (2 levels) in '

Finished in 0.09145 seconds
2 examples, 2 failures
Implementing the logic to achieve the goals by enhancing the models/person.rb:
class Person < ActiveRecord::Base
  attr_accessible :birthday, :name, :surname, :active
  validates_presence_of :name
end
Again the test:
Failures:

  1) Person should be activated before validation
     Failure/Error: @person.should be_active
       expected active? to return true, got false
     # ./spec/models/person_spec.rb:17:in `block (2 levels) in '

Finished in 0.17313 seconds
2 examples, 1 failure
First requirement is satisfied. Enhancing the same model to strike the second:
class Person < ActiveRecord::Base
  attr_accessible :birthday, :name, :surname
  validates_presence_of :name
  before_validation :activate

private
  def activate
    self.active = true
  end
end
Running the tests again:
Finished in 0.11393 seconds
2 examples, 0 failures
signals: mission accomplished!

Supported by Ruby 2.1, RubyOnRails 3.2.17 and RSpec 2.12.0