Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Create fixtures for unit testing as in Factory Girl but without database usage.

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 test
Octocat-spinner-32 Gemfile
Octocat-spinner-32 Gemfile.lock
Octocat-spinner-32 MIT-LICENCE
Octocat-spinner-32 README.rdoc
Octocat-spinner-32 Rakefile.rb
Octocat-spinner-32 factory_boy.gemspec
README.rdoc

Overview

/!\Version 2.0.2 is compatible with ActiveRecord < 3.1

/!\Version 2.0.3 is compatible with ActiveRecord >= 3.1

Factory Boy aims to avoid slow unit tests due to usage of create/find fixtures in database, with factory_girl for example.

Factory Boy can be used as factory_girl except that factories are not created in database.

ActiveRecord::Base finders are stubbed to return fixtures (plants) you have instanciated.

Now, Factory Boy 2 handle stub of Active Record (3+) queries. This means, the fixtures(plants) created with factory boy are retrieved via a AR queries(and only with AR new queries) of your models.

It does not pretend to stub 100% of all queries, but the coverage can be estimated at about 80%-90% of useful queries.

Active Record is stubbed only when at least one Plant is created in a test.

After each test everything is unstubbed. That means, if you have a case where a particular(complex) query is executed but not right stubbed with factory boy you can test using fixtures in databases(with factory girl or just model.create ..), skipping factory boy process.

Tested with ruby 1.9+, ActiveRecord 3.1(version 2.0.2) and ActiveRecord 3.1+ (version 2.0.3) Tests are suppose to use ActiveSupport::TestCase

See some examples below. You should see unit tests to inspect tested stubbed queries!

Quality Metrics

Continuous Integration

Queries it supposes to handle

  • where clauses on attributes and associations

  • chained where clauses

  • like sql predicate

  • limit, offset

  • order (with only one order clause)

  • ranges (ie where(:age => (20..30)))

  • IS NULL and IS NOT NULL sql predicates

  • dynamic finders (ie find_by_name_and_age(…)

  • scopes

The better way to see queries handled is to see all unit tests.

Queries NOT handled

  • Queries with explicit sql string(find_by_sql(“…”))

  • #order with more than one order clause (ie .order(name asc, age desc))

  • IS and IS NOT with other operand than NULL

  • having - group clauses methods

  • select clause method

  • lock

  • readonly

  • from

Ids

Each plant fixture has now an (unique) id.

Usage

Define your Plants (~ Factories if factory_girl) in test/plants.rb

Examples :

Plant.define :address do |address|
  address.number = 12
  address.street = "rue de Brest"
end

Plant.define :user do |user|
 user.name="Bart"
 user.age=800
 user.addresses = [Plant(:address)]
end

Get it with :

user = Plant(:user)

Example of tests :

def test___1
  address = Plant(:address, :street => 'rue des Lilas')
  user = Plant(:user, :name => 'Joe', :addresses => [address])

  assert_equal user, User.find #OK
  assert_equal user, User.find(:first) #OK
  assert_equal user, User.find(:last) #OK
  assert_equal [user], User.where(:name => 'Joe') #OK
  assert_equal [user], User.where("name = 'Joe' and addresses.street = 'rue des Lilas'").joins(':addresses) #OK
  assert_equal [address], user.addresses.where(:street => 'rue des Lilas') #OK

end

def test___2
  2.times { Plant(:user) }
  assert_equal 2, User.find(:all).size #OK
end

You can also create a particular plant of user like that:

user = Plant(:user, :name => "Marie", :age => age)

Specification of the class of the fixture definition

Plant.define :admin, :class => User do |user|
  user.name  = "Bart"
  user.age = 800
end

Associations

Assign fixtures to association in definition of plant :

Plant.define :profile do |profile|
  profile.password = "BREIZH!"
end

Plant.define :address do |address|
  address.number = 12
  address.street = "rue de Brest"
end

Plant.define :user do |user|
  user.name  = "Bart"
  user.age = 800
  user.profile = Plant(:profile)
  user.adresses = [Plant(:address)]
end

Definitions with dependent attributes

If you want to use the value of another attribute in definition, do like that :

Plant.define :user do |user|
  user.name = "Marie"
  user.adresses = [Plant(:address, :street => "Rue de #{user.name}")]
end

Sequences

As with factory_girl you are able to use sequences, like that :

Plant.sequence :email do |n|
  "incognito#{n}@kantena.com"
end

Plant.next(:email) # => "incognito1@kantena.com"
Plant.next(:email) # => "incognito2@kantena.com"

In Development

  • FIX issues with ruby 1.9.3

  • Stubs aggregations methods in queries(sum, count …)

  • Having - group clauses methods

  • Select clause method

  • Case when primary key is not id

  • Handle table name modified via table_name method

  • Handle foreign key specified name (ie foreign_key => ) in association declaration

Install

gem install factory_boy

Change Log

  • Compatibility with ActiveRecord >= 3.1

  • add require 'active_support/test_case' To fix constant not found Test (setup.rb) when ran as gem under rails 3 app

  • FIX : When : statements incompatibility with ruby 1.9.2

Note

If you use it, thanks to report feedbacks here, as issue or by git mail box, it will help me to enhance it. Thanks!

Something went wrong with that request. Please try again.