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