home icon contact icon rss icon

By: Matt Lins

Shoulda + Factory_Girl + Webrat for Rails Integration Tests

Is anyone else using shoulda, factory_girl and webrat for Rails integration tests?

I did a quick search and found Daniel Wellman using it in integration tests minus shoulda and factory_girl. I really like adding shoulda to get the contexts and factory_girl is a great replacement for fixtures. When combined with webrat it really makes for some easy to read integration tests. By the way, I'm already using shoulda and factory_girl for my unit and functional tests.

Here is an example I whipped up last night. It's nothing fancy. I'll probably make some modifications in the future, but so far I'm happy with it. I'm loading webrat in my test_helper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
require '../test_helper'

class AdminLoginTest < ActionController::IntegrationTest
  context 'An employee' do
    setup do
      @employee = Factory(:employee)
    end

    should 'be able to login with a valid username and password' do
      visit '/admin'
      assert https?
      assert_equal '/admin/sessions/new', path
      fills_in 'Email', :with => @employee.user.email
      fills_in 'Password', :with => @employee.user.password
      clicks_button 'Log In'
      assert_equal @employee.user.id, session[:user_id]
    end

    should 'be denied access with an invalid username or password' do
      visit '/admin'
      assert https?
      assert_equal '/admin/sessions/new', path
      fills_in 'Email', :with => @employee.user.email
      fills_in 'Password', :with => 'wrongpassord'
      clicks_button 'Log In'
      assert_nil session[:user_id]
    end
  end
end

This is testing a simple login to the admin section of a project I'm working on. We're using restful_authentication. Each line of each test is essentially an assertion. If something doesn't respond with a success or a form field is missing, the test fails.

By: Matt Lins

Factory_Girl Sequences as Lazy Attributes

I just ran into a problem with factory_girl sequences. The problem was I defined my Factories like this:

1
2
3
4
5
6
7
8
9
10
11
12
# Sequences

Factory.sequence :name do |n|
  'name' + n.to_s
end

# Manufacturers

Factory.define :manufacturer do |m|
  m.name Factory.next(:name)
  m.active true
end

Assume I have a validates_uniqueness_of validation on name in Manufacturer:

1
2
3
4
5
class Manufacturer < ActiveRecord::Base

  validates_uniqueness_of :name

end

Now, lets say I created a couple of manufacturers in my test setup:

1
2
3
4
5
6
7
8
9
require File.dirname(__FILE__) + '/../test_helper'

class ProductTest < ActiveSupport::TestCase

  setup do
    2.times { Factory(:manufactuer) }
  end

end

Ok, this setup block will fail because it's creating 2 manufacturers with the same name. But, I thought I was using a sequence? Well, the sequence is only sequenced during the definition of the factories. Meaning, in the above example Factory#next was only called once.

Luckily, factory_girl allows us to use something called lazy attributes. We can just wrap our sequence with some curly brackets, like so:


m.name { Factory.next(:name) }

Now everytime I create a new Manufacturer the sequence will generate a unique name. Nice! Maybe if I'd taken some more time to RTFM, I wouldn't have wasted time on this problem. Regardless, I thought maybe I'd be able to help someone else out that runs into this.

More about factory_girl

By: Matt Lins

Lose the fixtures with rSpec

rSpec is all about writing easy to read, self-explanatory specs (tests to you TDDr's). It's tempting to want to use fixtures or even come up with a clever way to provide sample data for testing models. I'll demonstrate, what I consider to be the most beautiful way to write specs. Some of my influences have been the rSpec Mailing list and the PeepCode Screencast. I'll warn you brilliant ruby programmers: this is by no means efficient, DRY code. It is code that any stranger could come along and figure out exactly what the original developer was trying to accomplish.

Assume we have a simple Model like this:

1
2
3
4
5
6
class Post < ActiveRecord::Base

  validates_presence_of :title
  validates_length_of :title, :minimum => 2

end

First we'll need the help of a couple of additional methods in Hash. Add this to your spec_helper.rb in the root of your /spec directory.

1
2
3
4
5
6
7
8
9
10
11
12
13
# Taken from 
# http://wincent.com/knowledge-base/Fixtures_considered_harmful%3F
class Hash
  # for excluding keys
  def except(*exclusions)
    self.reject { |key, value| exclusions.include? key.to_sym }
  end

  # for overriding keys
  def with(overrides = {})
    self.merge overrides
  end
end

These two methods with allow us to create a "valid_attributes" Hash and then tweak it later on. We're going to create a helper module at the top of the spec. This example is from one of my specs for a model: Post.

1
2
3
4
5
6
7
8
9
10
require File.dirname(__FILE__) + '/../spec_helper'
module PostSpecHelper
  def valid_post_attributes
    {
      :title => "rSpec is Great"
      :body => "rSpec is a fun way to write documentation 
        and test your application at the same time"
    }
  end
end

Now we have some nice sample data. We can use this helper(don't forget to include it) in the before(setup) block of each of our describe blocks. Let start with a simple valid Post.

1
2
3
4
5
6
7
8
9
10
describe Post do
  include PostSpecHelper
  before(:each) do
    @post = Post.new
    @post.attributes = valid_post_attributes
  end
  it do
    @post.should be_valid
  end
end

Great. Now let's use the add Hash#with to test one of our validations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe Post, "with a title that is 1 character long" do
  include PostSpecHelper
  before(:each) do
    @post = Post.new
    @post.attributes = valid_post_attributes.with(:title => "r")
  end

  it do
    @post.should_not be_valid
  end

  it "should have an error on title" do
    @post.should have(1).error_on(:title)
  end
end

Now, we'll use the Hash#except to test another validation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe Post, "with a blank title" do
  include PostSpecHelper
  before(:each) do
    @post = Post.new
    @post.attributes = valid_post_attributes.except(:title)
  end

  it do
    @post.should_not be_valid
  end

  it "should have an error on title" do
    @post.should have(1).error_on(:title)
  end
end

There you have it, an easy to read spec. Any developer could come along and figure out exactly what I was testing. The sample data is provided in the top of every spec. I don't have to go searching through fixtures to figure out why this test failed.