public
Description: A simple object factory that assists with creating valid model objects when writing specifications
Home | Edit | New

Home

STOP PRESS: Erk, it appears that this doesn’t work when used as a gem. Instead I’ve added an init.rb, so you can stick it in vendor/plugins and use it as a Rails plugin. I’ll figure it out and get it working as a gem as I don’t want it to be Rails-specific. (for those that are interested, it relies on a singleton Factory instance, which seems to get recreated on every invocation, when used as a gem).

Object Factories

Whilst mocks let you test controllers without worrying about the implementation of your models, sometimes, when testing models, or using Stories, you need to be able to generate a valid model without wanting to worry about the implementation details on that model.

For example, to test a validates_uniqueness_of constraint you would normally write:


  @first_user = User.create! :name => 'George', :password => '12345', :password_confirmation => '12345'
  @second_user = User.new :name => 'George'
  @second_user.should_not be_valid
  @second_user.should have(1).errors_on(:name)

The problem here is the first line.

What if the User model changes so that every user must have a telephone number? Suddenly that specification fails, as @first_user does not save; however the failure is nothing to do with that specification – our specification now depends upon information that is outside of the specification’s realm. And that makes for brittle tests that are hard to maintain.

Using the Object factory we can write:


  @first_user = a User, :name => 'George'
  @first_user.save!
  @second_user = a User, :name => 'George'
  @second_user.should_not be_valid
  @second_user.should have(1).errors_on(:name)

The a method (also aliased as an) creates a valid object of the given class every time – you can optionally override some of the fields if needs be. If you need your object saving then you can call a_saved instead (so the first two lines can be shortened to @first_user = a_saved User, :name => ‘George’).

So how does the Object factory know how to create a valid object? Easy, you tell it which fields are required for a given model (normally in your spec_helper.rb).

At its simplest, you can just create a new instance:


  @person = a Person, :first_name => 'John', :last_name => 'Smith'

But, if that Person needs to have a unique employee code, for example, we can pre-configure the object factory to build one for us:


  when_creating_a Person, :auto_generate => :employee_code
 
  @person = a Person, :first_name => 'John', :last_name => 'Smith'
  puts @person.employee_code # will show an auto-generated, unique value

However, we can override the auto-generated value if we need to as well:


  when_creating_a Person, :auto_generate => :employee_code
 
  @person = a Person, :first_name => 'John', :last_name => 'Smith', :employee_code => '12345'
  puts @person.employee_code # will show 12345

If the Person needs to supply a password and a password confirmation:


  when_creating_a Person, :auto_confirm => :password
 
  @person = a Person, :first_name => 'John', :last_name => 'Smith'
  puts @person.password
  puts @person.password_confirmation # will show the same auto-generated, unique value for both fields

Overall, you have the following options for each class:

  • :auto_generate => [:field1, :field2]: generates a unique string value for the given field name or array of field names
  • :auto_confirm => :password: generates a unique string value for the given field name or array of field names and also sets field_name_confirmation to the same value
  • :generate_email_address => :email: generates a randomised email address for the given field name or array of field names
  • :set => { :field3 => ‘value3’, :field4 => ‘value4’ }: sets the given fields to the supplied static values
  • :generate => { :field5 => lambda { Date.today } }: sets the given fields to the supplied dynamic values

Read more about using Object Factory with Rails

Last edited by rahoulb, Thu Mar 05 13:21:02 -0800 2009
Home | Edit | New
Versions: