GithubHelp home page GithubHelp logo

assembly_line's Introduction

Assembly Line

Assembly Line allows you to group together a series of rspec let statements which can later be evaluated to set up a specific state for your specs. It's an excellent compliment to factory_girl because you can use your factories to build up each component, then use AssemblyLine to bring them all together.

As a system grows in complexity, more objects are required to set up the necessary state to write tests. Sometimes you'll need to write a test that requires access to the beginning, middle, and end of a very large hierarchy, and AssemblyLine can expose each part of the hierarchy that you care about.

Installation

gem install assembly_line

Edit your spec/spec_helper.rb

# unnecessary if you used config.gem 'assembly_line'
require 'assembly_line'

# I put my definitions inside of spec/support/ because my spec_helper loads everything in that directory
require 'your_assembly_line_definitions'

# remember to extend the module in your Rspec configuration block
Spec::Runner.configure do |config|
  config.extend AssemblyLine
end

Example

Assuming you have the following class

class Party < Struct.new(:host, :attendees)
  attr_writer :drinks
  def drinks
    @drinks ||= []
  end
end

Place your AssemblyLine definitions in spec/support/assemblies/party_assembly.rb

AssemblyLine.define(:drinks) do
  let(:drinks) { [:gin, :vodka] }
end

AssemblyLine.define(:party) do
  depends_on :drinks

  let(:host) { :rochelle }
  let(:attendees) { [:nick, :ellis, :coach] }
  let(:party) { @party = Party.new(host, attendees) }
  let(:party_crasher) { attendees << :crasher; :crasher }
end

Use your AssemblyLine in a test

describe Party do
  Assemble(:party)
  before do
    puts "simple before block"
  end

  context "attendees" do
    it "does not count the host as an attendee" do
      party.attendees.should_not include(host)
    end
  end

  context "party crasher" do
    Assemble(:party).invoke(:party_crasher)

    it "exemplifies method invocation after assembly" do
      party.attendees.should include(:crasher)
    end
  end

  context "drinks" do
    it "does not include the list of standard drinks" do
      party.drinks.should_not include(drinks)
    end
  end
end

Use your AssemblyLine in an irb session

AssemblyLine works a little differently when using it in irb. Your let definitions will not be defined globally (see e26a903), instead you'll have to prefix all defined methods with AssemblyLine.

>> require 'spec/support/assemblies'
>> Assemble(:drinks)
=> #<AssemblyLine::Constructor:0x10049e958 @code_block=#<Proc:0x000000010049ea98@(irb):1>, @name=:drinks, @rspec_context=AssemblyLine::GlobalContext, @options={}>
>>
>> AssemblyLine.drinks # the `drinks` method is prefixed with AssemblyLine
=> [:gin, :vodka]

Usage

let

AssemblyLine.define(:drinks) do
  let(:drinks) { [:gin, :vodka] }
end

This is the main usage of AssemblyLine. Let defines a method named after the first parameter and returns the return value of the provided block. Let memoizes the results so subsequent calls to the method will return the same result without re-running the block. Let delegates to rspec's implementation of let.

before

AssemblyLine.define(:killer_feature) do
  let(:killer_feature) { KillerFeature.new }
  before do
    KillerFeature.delete_all
    killer_feature.save!
  end
end

You can use rspec's before blocks within an AssemblyLine definition. This is a simple delegate so before, before(:each), and before(:all) are all supported. You can even reference your let definitions within a before block.

depends_on

AssemblyLine.define(:user_with_expired_cc) do
  let(:user) do
    Factory(:confirmed_user).tap do |u|
      u.credit_card.update_attribute :expiration_date, 1.year.ago
    end
  end
end

AssemblyLine.define(:subscription_with_invalid_cc) do
  depends_on :user_with_expired_cc
  let(:subscription) { Factory(:subscription, :user => user) }
end

You can utilize other AssemblyLine definitions with depends_on. It takes a list of AssemblyLine definitions to load. This allows you to make modular AssemblyLines which can be used throughout your spec suite.

invoke

AssemblyLine.define(:drinks) do
  let(:drinks) do
    [ Factory(:drink, :name => :gin), Factory(:drink, :name => :vodka) ]
  end
end

describe "Drinks#index" do
  Assemble(:drinks).invoke
  it "lists the drinks" do
    visit drinks_path
    response.should contain(drinks.first.name)
    response.should contain(drinks.last.name)
  end
end

Invoke takes a list of method names to call after assembly. If no arguments are provided, AssemblyLine will call a method named after the AssemblyLine itself, in this case drinks will be called. Note, these methods are called in a before(:all) block.

Let definitions are lazily evaluated which means sometimes the expected data isn't available until it's too late. In the above example, if I didn't call invoke the drinks index page would be empty.

Thanks!

  • l4rk (initial spike declaring modules and including them in the rspec context)
  • veezus (code contributions, introduced modular design / dependencies)
  • bigtiger (named the project, contributor)
  • leshill (support and testing, suggested irb support)
  • wgibbs (suggested irb support)

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright

Copyright (c) 2010 Sandro Turriate. See MIT_LICENSE for details.

assembly_line's People

Contributors

sandro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

bigtiger

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.