Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Makes FactoryGirl easy and automated. deep_* methods for automating FactoryGirl creation with required association trees and small tweaks, and a nice flexible FactoryGirl factories code generator.
Ruby

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
bin
lib
.gitignore
LICENSE
README.md
Rakefile
stepford.gemspec

README.md

Stepford

Now getting started with FactoryGirl is even more simple and DRY!

Stepford is an automatic required (non-null or presence validated) association resolver and factory generator for FactoryGirl.

The Stepford CLI allows can generate a factories.rb or multiple files each defining a single factory, for every existing model or for those specified.

The following would create/overwrite test/factories.rb with a factory for every model in app/models:

bundle exec stepford factories

If you use rspec, it would be:

bundle exec stepford factories --path spec

With our rspec helper, you can use this to create a bar and automatically create its dependencies and their dependencies, etc. providing ways to remedy circular dependencies:

deep_create(:bar)

You can also create_list, build, build_list, and build_stubbed:

deep_build_list(:bar, 5)

Need to customize it? You can use the normal FactoryGirl behavior (args, options, block), but in addition, you may specify options for each factories that would create direct or indirect associations.

e.g. maybe Bar has a required association called house_special which uses the beer factory, and we have a block we want to send into it, and Beer has specials that you want to build as a list of 3, using the tuesday_special_offer factory. In rspec, you'd do:

deep_create_list(:bar, with_factory_options: {
  house_special: [:create, :beer, {blk: ->(beer) do; beer.bubbles.create(attributes_for(:bubbles)); end}],
  specials: [:build_list, :tuesday_special_offer, 3]
}) do
  # any block you would send to FactoryGirl.create_list(:bar) would go here
end

By default autogenerated factories just have required attributes, e.g.:

require 'factory_girl_rails'

FactoryGirl.define do

  factory :novel do
    created_at { 2.weeks.ago }
    name 'Test Name'
    price 1.23
    sequence(:isbn)
    sequence(:ean)
    trait :with_summary do; template 'Test Summary'; end
    updated_at { 2.weeks.ago }
  end

end

But you can have it autogenerate lots of the FactoryGirl bells and whistles:

require 'factory_girl_rails'

FactoryGirl.define do

  factory :novel do
    author
    association :edited_by, factory: :user
    FactoryGirl.create_list :comments, 2
    trait :with_notes do; FactoryGirl.create_list :note, 2; end
    trait :complete do; complete true; end
    trait :not_complete do; complete false; end
    created_at { 2.weeks.ago }
    name 'Test Name'
    price 1.23
    sequence(:isbn)
    sequence(:ean)
    trait :with_summary do; template 'Test Summary'; end
    updated_at { 2.weeks.ago }
  end

end

But, everything from author to with_notes may have association interdependency issues unless you hand edit the generated versions.

Stepford's FactoryGirl (and optionally its rspec helper) can help you avoid the heavy lifting.

Setup

In your Rails 3+ project, add this to your Gemfile:

gem 'stepford'

If you don't already have it, add this also:

gem 'factory_girl_rails'

Then run:

bundle install

Usage

Require

Put this in your test/spec_helper.rb, spec/spec_helper.rb, or some other file used by your tests:

require 'stepford/factory_girl'

Stepford::FactoryGirl

Stepford::FactoryGirl acts just like FactoryGirl, but it goes through all the null=false associations for foreign keys that aren't primary keys in the factory and/or its presence validated associations and attempts to create/build/build_stub depending on what you called originally, but also lets you pass in an :with_factory_options that can contain a hash of factory name symbols to the arguments and block you'd pass to it. You specify the block using a :blk option with a proc/lambda (probably a lambda) to use in that method.

If you don't specify options, it's easy (note: it is even easier with the rspec helper- see below). If Foo requires Bar and Bar requires a list of Foobars and a Barfoo, and you have factories for each of those, you'd only have to do:

Stepford::FactoryGirl.create_list(:foo, 5)

and that would create a list of 5 Foos, that each have a Bar, where each Bar has a list of 2 Foobars and a Barfoo. Easy!

But, you might want to specify traits, and certain attributes or associations or a block or different methods to use. That's pretty easy, too. Let's say you only need to tweak bar and foobar on each item, but the rest gets created as it would with just Stepford::FactoryGirl.create_list, so if you wanted to create 5 with two traits :fancy and :light and only build the bar and build bar's foobar as a stub:

Stepford::FactoryGirl.create_list(:foo, 5, :fancy, :light, with_factory_options: {
  bar: [:build, :bar],
  foobar: [:build_stubbed, :foobar]
}) do
  # any block you would send to FactoryGirl.create_list(:foo) would go here
end
RSpec Helpers

Put this in your spec/spec_helper.rb:

require 'stepford/factory_girl_rspec_helpers'

Then you can just use deep_create, deep_create_list, deep_build, deep_build_list, or deep_build_stubbed in your rspec tests (deep_create becomes a shortcut for ::Stepford::FactoryGirl.create, etc.), e.g.:

deep_create(:foo)
Debugging

Add somewhere after the require:

Stepford::FactoryGirl.debug = true

CLI

Stepford has a CLI with a circular reference checker and a generator to automatically create your factories file(s).

Refs

Check ActiveRecord circular dependencies find circular chains of dependencies where foreign keys that are not primary keys of the models are all not nullable in the schema or not nullable because of ActiveRecord presence validation:

bundle exec stepford circular

Example of output:

The following non-nullable foreign keys used in ActiveRecord model associations are involved in circular dependencies:

beers.waitress_id -> waitresses.bartender_id -> bartenders.beer_id -> beers.waitress_id

beers.waitress_id -> waitresses.bartender_id -> bartenders.order_id -> order.beer_id -> beers.waitress_id


Distinct foreign keys involved in a circular dependency:

beers.waitress_id
order.beer_id
bartenders.beer_id
bartenders.order_id
waitresses.bartender_id


Foreign keys by number of circular dependency chains involved with:

2 (out of 2): beers.waitress_id -> waitresses
2 (out of 2): waitresses.bartender_id -> bartenders
1 (out of 2): order.beer_id -> beers
1 (out of 2): bartenders.order_id -> order
1 (out of 2): bartenders.beer_id -> beers
Factories
Creating Factories

To autogenerate test/factories.rb from all model files in app/models:

bundle exec stepford factories

If you want one file per model, specify --multiple. Use --path to specify the directory path or factories.rb pathname. The default path is test/factories, which it assumes exists. In that directory, it will create a factory file for each model. If you want separate factory files in spec/factories, you'd use:

bundle exec stepford factories --path spec/factories --multiple
RSpec

To put all of your factories into spec/factories.rb:

bundle exec stepford factories --path spec

This also works:

bundle exec stepford factories --path spec/support/factories.rb
Specifying Models

By default, Stepford processes all models found in app/models.

Specify --models and a comma-delimited list of models to only output the models you specify. If you don't want to overwrite existing factory files, you should direct the output to another file and manually copy each in:

bundle exec stepford factories --path spec/support/put_into_factories.rb --models foo,bar,foo_bar
Associations

If you use Stepford::FactoryGirl (or deep_* methods in rspec) to automatically generate factories, you may not need to generate associations, because that sets them for you. If you do choose to use associations, note that these will likely create factories with interdependence issues. When there are NOT NULLs on foreign keys and/or presence validations, etc. you can't just use after(:create) or after(:build) to set associations, and without those you can have issues with "Trait not registered" or "Factory not registered". Later versions of FactoryGirl may make this easier, and be sure to see notes from Josh down in the troubleshooting section.

If you are ready to hand-edit to fix things, then copy paste stuff, rename it, etc. instead of just using Stepford::FactoryGirl (or deep_* methods in rspec), then keep reading.

# Include Required Assocations

To include NOT NULL foreign key associations or presence validated associations:

bundle exec stepford factories --include-required-associations
# Include All Associations

To include all associations even if they aren't deemed to be required by not null ActiveRecord constraints defined in the model:

bundle exec stepford factories --associations
# Checking Model Associations

If --associations or --validate-associations is specified, Stepford first loads Rails and attempts to check your models for broken associations.

If associations are deemed broken, it will output proposed changes.

No IDs

If working with a legacy schema, you may have models with foreign_key columns that you don't have associations defined for in the model. If that is the case, we don't want to assign arbitrary integers to them and try to create a record. If that is the case, try --exclude-all-ids, which will exclude those ids as attributes defined in the factories and you can add associations as needed to get things working.

Traits

To generate traits for each attribute that would be included with --attributes, but isn't because --attributes is not specified:

bundle exec stepford factories --attribute-traits

To generate traits for each association that would be included with --associations, but isn't because --associations is not specified:

bundle exec stepford factories --association-traits
Constraints and Validations

If the ActiveRecord column null property for the attribute is true for the attribute or foreign key for the association, or if there is a presence validator for an attribute or foreign key for the association, then that attribute or association will be defined by the default factory.

Uniqueness constraints on the model are handled by the following being generated in the factory, which works for strings and numbers:

sequence(:my_attribute)

If you have a formatting constraint, some other constraint, or don't like the format of the data in the factories, see the Factory Girl documentation to find out how to customize your factories.

Testing Factories

See Testing all Factories (with RSpec) in the FactoryGirl wiki.

Here are a few rspecs that test the FactoryGirl factories and the Stepford deep_builds:

require 'spec_helper'
require 'stepford/factory_girl_rspec_helpers'

describe 'validate factories build' do
  FactoryGirl.factories.each do |factory|
    context "with factory for :#{factory.name}" do
      subject { build(factory.name) }

      it "is valid" do
        subject.valid?.should be, subject.errors.full_messages
      end
    end
  end
end

describe 'validate factories deep build' do
  FactoryGirl.factories.each do |factory|
    context "with factory for :#{factory.name}" do
      subject { deep_build(factory.name) }

      it "is valid" do
        subject.valid?.should be, subject.errors.full_messages
      end
    end
  end
end
Troubleshooting

If you have duplicate factory definitions during Rails load, it may complain. Just move, rename, or remove the offending files and factories and retry.

The factories CLI produces factories that use Ruby 1.9 hash syntax. If you aren't using Ruby 1.9, it may not fail during generation, but it might later when loading the factories.

If you are using STI, you'll need to manually fix the value that goes into the type attribute.

If you use Stepford to create factories for existing tests and the tests fail with something like:

 ActiveRecord::StatementInvalid:
   PG::Error: ERROR:  null value in column "something_id" violates not-null constraint

or maybe:

 ActiveRecord::RecordInvalid:
   Validation failed: Item The item is required., Pricer The pricer is required., Purchased by A purchaser is required.

then try to use the deep_* methods to build or create.

If you get:

SystemStackError:
  stack level too deep

then note that associations and traits can lead to circular dependencies. Trying generating factories without associations or traits (the default), and use the deep_* methods to create.

If you still see the 'stack level too deep' error, use the circular CLI to find interreferencing non-nullable foreign keys and fix them.

ThoughtBot's Josh Clayton provided some suggestions for this, including using methods to generate more complex object structures:

def post_containing_comment_by_author
  author = FactoryGirl.create(:user)
  post = FactoryGirl.create(:post)
  FactoryGirl.create_list(:comment, 3)
  FactoryGirl.create(:comment, author: author, post: post)
  post.reload
end

or referring to created objects through associations, though he said multiple nestings get tricky:

factory :post do
  author
  title 'Ruby is fun'
end

factory :comment do
  author
  post
  body 'I love Ruby too!'

  trait :authored_by_post_author do
    author { post.author }
  end
end

comment = FactoryGirl.create(:comment, :authored_by_post_author)
comment.author == comment.post.author # true

This is the reason we wrote the Stepford's Factory Girl proxy and helper rspec methods (see above). It automatically determines what needs to be set in what order and does create, create_list or build, build_list, etc. automatically.

License

Copyright (c) 2012 Gary S. Weaver, released under the MIT license.

Something went wrong with that request. Please try again.