Carpenter
Description
Carpenter provides syntax sugar for using the Test Data Builder pattern in Ruby testing frameworks. It complements FactoryGirl and mock objects.
Why?
Sometimes, instantiating a test object is really ugly. For example, Value Objects will very often have long declarations.
Webbed::Method.new("GET", :safe => true, :idempotent => true, :allows_request_entity => false, :allows_response_entity => true)
Wouldn't it be nice if we could put this ugliness in a separate file? Carpenter allows you to define "builders" which can be reused in different test files.
# in spec/builders/method.rb
Carpenter.define do
builder :get_method do
Webbed::Method.new("GET", :safe => true, :idempotent => true, :allows_request_entity => false, :allows_response_entity => true)
end
end
# in spec/webbed/method_spec.rb
Carpenter(:get_method) # => a new Webbed::Method
This pattern looks similar to FactoryGirl; however, Carpenter is not a substitute for FactoryGirl and its alternatives. FactoryGirl was designed to generate ActiveRecord models. Carpenter was designed to generate any kind of Ruby object. Prefer FactoryGirl over Carpenter whenever possible.
Carpenter is also not a substitute for mock objects. Build objects using Carpenter if they are the object currently under test. Use mocks or stubs if you are not testing the object, but instead testing one of its associates.
Installation
First, install the gem.
gem install carpenter
For RSpec users, add the following to your spec_helper.rb
:
require "carpenter"
Carpenter::BuilderLoader.load_all
RSpec.configure do |config|
config.include Carpenter::BuildDSL
end
For Test::Unit
users, add the following to your test_helper.rb
:
require "carpenter"
Carpenter::BuilderLoader.load_all
class Test::Unit::TestCase
include Carpenter::BuildDSL
end
Other testing frameworks should work with Carpenter if they support including a
Ruby module. All you have to do is call Carpenter::BuilderLoader.load_all
and include
Carpenter::BuildDSL
into the framework. Please submit a GitHub if there are
any issues with some frameworks.
Usage
By default, Carpenter loads all the files from test/builders
and
spec/builders
. I'd suggest making a builder file for each class that you
build. Each file should be wrapped in a Carpenter.define
block like so:
# in spec/builders/my_object.rb
Carpenter.define do
# Your builders go here.
end
You can define builders using the #builder
method. It takes a symbol as the
name of the builder and a block as the actual builder. Here's a really simple
builder that will make a new instance of MyObject
with some arguments:
# in spec/builders/my_object.rb
Carpenter.define do
builder :my_object do
MyObject.new(:foo, :bar)
end
end
Builders also can take an optional hash of options to let you configure the test data a bit.
# in spec/builders/my_object.rb
Carpenter.define do
builder :my_object do ||
MyObject.new([:foo], [:bar])
end
end
Builders can be named whatever you want and return whatever you want. They're essentially methods. So, this is just as valid as the last example, albeit a bit confusing:
# in spec/builders/abraham_lincoln.rb
Carpenter.define do
builder :abraham_lincoln_quote do
"The trouble with quotes on the Internet is that you can never know if they are genuine."
end
end
To use your builders in your tests, just use the #Carpenter
method with the
name of the builder and (optionally) a hash of options.
# in spec/my_object_spec.rb
describe MyObject do
it "can be created" do
Carpenter(:my_object).should_not be_nil
end
end
You can add more builder load directories by adding them to the builder loader's builder paths before loading the builders.
Carpenter::BuilderLoader.builder_paths << "spec/custom_builder_dir"
Carpenter::BuilderLoader.load_all
License
Released under the MIT License. See the LICENSE file for details.