Blueprints

Awesome replacement for factories and fixtures that focuses on being DRY and making developers type as little as possible.

Setup

The easiest way to install this gem for Ruby on Rails is just add this line to config/environment.rb (or config/environments/test.rb):

config.gem 'blueprints'

If you’re not using rails, then you can install it through command line

sudo gem install blueprints

Lastly you could use it as plugin:

ruby script/plugin install git://github.com/sinsiliux/blueprints.git

Blueprints is activated by calling Blueprints.enable at the bottom of your spec_helper/test_helper. If you’re using RSpec make sure you call Blueprints.enable after requiring RSpec, otherwise it will lead to strange behaviour. This method accepts block and yields Blueprints::Configuration object.

These options can be set on blueprint configuration object:

  • root - custom framework root if automatic detection fails for some reason (eg. not rails/merb project)

  • filename - custom patterns of files that contain your blueprints (in case one of automatic ones doesn’t fit your needs)

  • prebuild - list of blueprints that should be preloaded (available in all tests, never reloaded so they’re much faster)

  • orm - allows to set ORM (currently can be :active_record or nil). Defaults to :active_record.

  • transactions - set this to false if you don’t want to use transactions. This will severely slow the tests but sometimes transactions can’t be used.

Sample usage:

Blueprints.enable do |config|
  config.filename = 'my_blueprints.rb'
  config.prebuild = :preloaded_blueprint
end

Blueprints file

Blueprints file is the file that contains all definitions of blueprints. This can either be single file or whole folder if you have many blueprints.

By default blueprints are searched in these files in this particular order in application root (which is either RAILS_ROOT if it’s defined or current folder by default):

  • blueprint.rb

  • blueprint/*.rb

  • spec/blueprint.rb

  • spec/blueprint/*.rb

  • test/blueprint.rb

  • test/blueprint/*.rb

You can set root option to override application root and filename option to pass custom filename pattern.

Usage

Definitions of blueprints look like this:

blueprint :apple do
  Fruit.blueprint :species => 'apple'
end

blueprint :orange do
  Fruit.create! :species => 'orange'
end

blueprint :fruitbowl => [:apple, :orange] do
  @fruits = [@apple,@orange]
  FruitBowl.blueprint :fruits => @fruits
end

Kitchen.blueprint :kitchen, :fruitbowl => d(:fruitbowl)

…and you use them in specs/tests like this:

describe Fruit, "apple" do
  before do
    build :apple
  end

  it "should be an apple" do
    @apple.species.should == 'apple'
  end
end

describe FruitBowl, "with and apple and an orange" do
  before do
    build :fruitbowl
  end

  it "should have 2 fruits" do
    @fruits.should == [@apple, @orange]
    @fruitbowl.should have(2).fruits
  end
end

Result of ‘blueprint’ block is assigned to an instance variable with the same name. You can also assign your own instance variables inside ‘blueprint’ block and they will be accessible in tests that build this blueprint.

Instead of SomeModel.create! you can also use SomeModel.blueprint, which does the same thing but also bypasses attr_protected and attr_accessible restrictions (which is what you usually want in tests).

All blueprints are run only once, no matter how many times they were called, meaning that you don’t need to worry about duplicating data.

Shorthands

There’s a shorthand for these type of scenarios:

blueprint :something do
  @something = SomeModel.blueprint :field => 'value'
end

You can just type:

SomeModel.blueprint :something, :field => 'value'

If you need to make associations then:

SomeModel.blueprint(:something, :associated_column => d(:some_blueprint))

…or if the name of blueprint and the name of instance variable are not the same:

SomeModel.blueprint(:something, :associated_column => d(:some_blueprint, :some_instance_variable))

…and when you need to pass options to associated blueprint:

SomeModel.blueprint(:something, :associated_column => d(:some_blueprint, :option => 'value'))

You can learn more about blueprint method in wiki.github.com/sinsiliux/blueprints/method-blueprint

Advanced Usage

Its just ruby, right? So go nuts:

1.upto(9) do |i|
  blueprint("user_#{i}") do
    User.blueprint :name => "user#{i}"
  end
end

You can also read more about advanced usages in wiki.github.com/sinsiliux/blueprints/advanced-usages

Transactions

Blueprints by default is transactional, meaning that before every test transaction is started and after every test that transaction is dropped which resets database to the state before the test. This state is empty database + any scenarios that you specify in enable_blueprints.

TODO

  • Add preloading blueprints for whole block of tests.

  • Fix rake tasks

  • Add merb support

  • Add support for other test frameworks

Credits

Andrius Chamentauskas <[email protected]>

The code is based on hornsby scenario plugin by Lachie Cox, which is based on Err’s code found in this post: errtheblog.com/post/7708

License

MIT, see LICENCE