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

Dissecting Rails Validations - Part II

The loading of the built-in validators

Well, it looks as if my posts will be irrespective of the actual chronology of events involved in validation. At least from a Rails start-to-finish call stack perspective. This post will venture back before those events discussed in Part I. The reason being, I forgot to mention how the Rails built-in validators are loaded and executed.

When your model's class definition is encountered(during initialization), all of the method calls to the built-in validators are ran. Let's say we have a model that looks like this:

1
2
3
class Supplier < ActiveRecord::Base
  validates_uniqueness_of :name   
end

When #validates_uniqueness_of is called, it jumps to ActiveRecord::Validations::ClassMethods, which was mixed in with ActiveRecord::Base during initilization earlier. We're not going to look at this method in whole because it's too long. The important part is that it makes use of #validates_each. I'd like to talk about #validates_each because it is the heart of almost all the built-in validators(some can't use it). Here it is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def validates_each(*attrs)
  options = attrs.last.is_a?(Hash) ? attrs.pop.symbolize_keys : {}
  attrs   = attrs.flatten

  # Declare the validation.
  send(validation_method(options[:on] || :save)) do |record|
    # Don't validate when there is an :if condition and that condition is false
    unless options[:if] && !evaluate_condition(options[:if], record)
      attrs.each do |attr|
        value = record.send(attr)
        next if value.nil? && options[:allow_nil]
        yield record, attr, value
      end
    end
  end
end

After the first couple of lines you see this:


send(validation_method(options[:on] || :save)) do |record|

This line of code is using the Ruby #send method to make a dynamic call to whatever #validation_method returns. Let's look at it:

1
2
3
4
5
6
7
def validation_method(on)
  case on
    when :save   then :validate
    when :create then :validate_on_create
    when :update then :validate_on_update
  end
end

When you declare your validation in your model, :on is an optional parameter. So, if we only wanted to run our validation on only the creation of records, we could do this:

1
2
3
class Supplier &gt; ActiveRecord::Base
  validates_uniqueness_of :name, :on => :create   
end

The #validation_method method will tell the #send method back in #validates_each where to declare the validator. Assuming we pass don't pass anything for the :on parameter it'll be declared by #validate(both create and update). Don't get these methods confused with low-level #validate, #validate_on_create and #validate_on_update of which you can override in your models. These are defined outside of Validations::ClassMethods and they have a different amount of parameters. Lets look at Validations::ClassMethods#validate:

1
2
3
4
def validate(*methods, &block)
  methods << block if block_given?
  write_inheritable_set(:validate, methods)
end

Validators can be methods, classes or blocks. This method will handle whatever you throw at it and toss it onto the array of validations to be ran whenever #valid? is called. It's using a method called #write_inheritable_set to store the array (read more about it in the inheritable_attributes.rb file).

Now, if you remember correctly from Part I the ActiveRecord::Base#save call is actually aliased to ActiveRecord::Validations#save_with_validation. This method makes a call to ActiveRecord::Validations#valid?. Let's dig into it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def valid?
  errors.clear

  run_validations(:validate)
  validate

  if new_record?
    run_validations(:validate_on_create)
    validate_on_create
  else
    run_validations(:validate_on_update)
    validate_on_update
  end

  errors.empty?
end

First, it clears the errors object. The errors object is defined by the ActiveRecord::Errors class, which mixes in Enumerable. You can add errors with #add and clear them with #clear etc. Anytime a validator encounters a problem with your model it adds an error. Next, #valid? calls #run_validations with :validate as a parameter(validation_method). Remember, these are validations that are called on both create and update. Continuing, it determines whether the record is new, it calls #run_validations again with the appropriate parameter(validation_method). Lastly, it returns the result of the expression errors.empty?. So, if any problems occurred during #run_validations, then the validator would have added an error to the enumerable causing #valid? to return false. Let's look at #run_validations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def run_validations(validation_method)
  validations = self.class.read_inheritable_attribute(validation_method.to_sym)
  if validations.nil? then return end
  validations.each do |validation|
    if validation.is_a?(Symbol)
      self.send(validation)
    elsif validation.is_a?(String)
      eval(validation, binding)
    elsif validation_block?(validation)
      validation.call(self)
    elsif validation_class?(validation, validation_method)
      validation.send(validation_method, self)
    else
      raise(
        ActiveRecordError,
        "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
         "class implementing a static validation method"
      )
    end
  end
end

It grabs all the validations based on the validation_method passed using the #read_inheritable_attribute (look in the inheritable_attributes.rb file to find out why this is used). Next, it iterates through each of the validations. It does a check to see if the validation is a method, class or a block and executes it appropriately. Remember, if these validations encounter problems with the model data, they'll add an error onto the @errors enumerable causing #valid? to return false. Let's tie it all together with a review of the #save_with_validation method:

1
2
3
4
5
6
7
def save_with_validation(perform_validation = true)
  if perform_validation && valid? || !perform_validation
    save_without_validation
  else
    false
  end
end

If #valid? returns true(and you didn't pass false to #save), then #save_with_validation makes a call to #save_without_validation, which is actually aliased back to ActiveRecord::Base#save thus completing our save. Remember, ActiveRecord::Base#save is actually aliased to #save_with_validation so that validations can happen. Confused? Well, this is done with #alias_method_chain. Also, you may need to review Part I.

That should do it for another part to this series. If I feel the need to write Part III, we'll probably dive into what each of the built-in validators is actually doing.