Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Machinery to create object graphs.
Ruby
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
spec
.gitignore
MIT-LICENSE
README.markdown
Rakefile
init.rb
machinery.gemspec

README.markdown

Machinery

Machinery to create object graphs and speed up tests.

Machinery helps to create object graphs for use in tests without needing fixtures, using pure ruby style. These object graphs are easily re-usable in Test::Unit, RSpec, Cucumber and in rake tasks to populate a database.

Secondly, even if you don't need object graphs, Machinery can speed up your tests significantly because you can set up any model objects in a before(:all) instead of a before(:each), and at the end of the tests all activerecord objects created in the before(:all) will be cleaned up automatically.

Machinery works with plain ruby objects and currently supports ActiveRecord. A DataMapper abstraction should be too easy.

Usage

Object Graphs

In /spec/scenarios.rb

Machinery.define do
  scenario :earth_ships do
    @titanic = Ship.create!(:name => "Titanic")
    @pirate  = Ship.create!(:name => "Pirate")
  end

  scenario :space_ships do
    @apollo = Ship.create!(:name => "Apollo")
    @uss_enterprise = Ship.create!(:name => "USS Enterprise")
  end

  scenario :all_ships do
    scenarios :earth_ships, :space_ships
    @sunken_ship = Ship.create!(:name => "Sunk")
  end
end

In /spec/spec_helper.rb

require 'machinery'
require File.join(File.dirname(__FILE__) + 'scenarios')

In /spec/models/ship_spec.rb

describe Ship do
  scenarios :all_ships

  it "should create a pirate ship" do
    @pirate.should_not be_nil
  end

  it "should have 5 ships" do
    Ship.count.should == 5
  end
end

Just the speed up please

If it's only speeding up your tests you're after you can use anonymous scenarios, like this:

In /spec/models/ship_spec.rb

describe Ship do
  scenario do
    @pirate = Ship.create!(:name => "Pirate")
  end

  it "should update a pirate ship" do
    @pirate.name = "Updated Pirate"
    @pirate.save
    @pirate.reload
    @pirate.name.should == "Updated Pirate"
  end

  it "should have 1 ship" do
    Ship.count.should == 1
  end

  it "should be named 'Pirate'" do
    @pirate.name.should == "Pirate"
  end
end

This will create one ship only in the database.

Explain

When you use +scenario+ with a block you are defining a scenario. Any instance variables you declare within a scenario will be available in your test when you use that scenario.

When you use +scenario+ without a block then you indicate that the block for the scenario you previously defined should be executed. Any instance variables you defined within the block will now be available to you in your test methods. Note that you can re-use scenarios within scenarios.

When you declare to use a scenario within an rspec +ExampleGroup+ (as in the first ship_spec example above) then the instances will be created for you exactly once. They will automatically be removed from the database after all the tests have run. Any instance variables declared within a scenario will be cloned before each test. If you make sure you use +use_transactional_fixtures+ then this means:

  • speedy tests, because instances are created exactly once for all tests
  • you can mess around with the instance variables any way you like within a test method as before each test they will always be in exactly the same initial state.

A special case is when you use an anonymous +scenario+ with a block, specified at the top of your tests (as in the second ship_spec example above). It will behave as if it's a named scenario and it is used immediately. At most one anonymous +scenario+ per example group can be used.

Magic?

Why write stuff like:

users(:admin).should be_nil

when you can just as well write:

@user_admin.should be_nil

Use less magic. Less code. Makes software softer.

Installation

Rails installation (Test::Unit)

As a Gem

Use this if you prefer to use versioned releases of Machinery. Specify the gem dependency in your config/environments/test.rb file:

Rails::Initializer.run do |config|
  config.gem "machinery", :lib => false, :source => 'http://gemcutter.org'
end

Then:

$ rake gems:install
$ rake gems:unpack

As a Plugin

Use this if you prefer to use the edge version of Machinery:

$ script/plugin install git://github.com/lawrencepit/machinery.git

Rails installation (RSpec)

If you're using Machinery with RSpec, it is recommended that you add config.gem lines for RSpec and Machinery in your config/environments/test.rb file, but do not ask Rails to load the RSpec and Machinery libraries:

config.gem 'rspec', :lib => false
config.gem 'rspec-rails', :lib => false
config.gem 'machinery', :lib => false, :source => 'http://gemcutter.org'

Then require machinery from your spec/spec_helper.rb file, before Spec::Runner is configured:

# requires for RSpec
require 'machinery'
Spec::Runner.configure do |config|
# ...

You should not need to require anything besides the top-level machinery library.

Credits

Written by Lawrence Pit.

Thanks to Machinist, ModelStubbing, FactoryGirl, FixtureScenarios and Hornsby for inspiration.

After using model_stubbing on a relatively large project for over a year I decided a simpler solution was needed. Object graphs need to be easily re-usable by specs, unit tests, cucumber stories and ad-hoc database loading. And tests need to be speedy.

Machinery works especially pleasurable in combination with Machinist or FactoryGirl.


Copyright (c) 2009-2011 Lawrence Pit, released under the MIT license

Something went wrong with that request. Please try again.