Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

highly experimental, lightweight and flexible contexts for test/unit

tree: 58b84c81cf

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 test
Octocat-spinner-32 README.textile
Octocat-spinner-32 demo.rb
README.textile

With

HIGHLY EXPERIMENTAL!

Heavily inspired by both
context
by Jeremy McAnally and
context_on_crack
(as well as rspec_on_rails_on_crack)
by Rick Olson.

For some demo code see: runnable demo controller test.
Style and initial motivation: http://gist.github.com/30436.

In short the goal of this library is:

  1. setup preconditions for tests/assertions in a more flexible and concise way
  2. run as many assertions with common preconditions (setup/before) at once as possible

So it allows to write test/unit tests like this:


  describe 'POST to :create' do
    action { post :create, @params }
  
    with :login_as_admin do
      it_assigns :article

      it "succeeds", :with => :valid_article_params do
        it_redirects_to 'articles/1'
        # more assertions ...
      end
      
      it "fails", :with => :invalid_article_params do
        it_assigns_flash :error, :not_nil
        # more assertions ...
      end
    end
  
    with :login_as_user, :no_login do
      it_redirects_to '/login'
    end
  end

This piece of code will generate at least 4 tests:


  test_POST_to_create_with_login_as_admin_and_with_valid_article_params
  test_POST_to_create_with_login_as_admin_and_with_invalid_article_params
  test_POST_to_create_with_login_as_user
  test_POST_to_create_with_no_login
  # TODO test method names should include the assertion block names (e.g. "succeeds")

Shared context blocks can be setup like this:


  share :login_as_admin do 
    before { @controller.current_user = Admin.new }
  end
  
  share :valid_article_params do
    # could also use a tool like FactoryGirl, Mechanist etc.here
    before { @params = {'title' => 'article title', 'body' => 'article body'} } 
  end

Also, one can map a context name like :invalid_article_params to multiple
blocks. With will then expand them and create separate tests for
them.

E.g. with a definition like:


  # share multiple contexts with the same name:  
  share :invalid_article_params do
    before { @params = valid_article_params.except(:title) }
  end
  
  share :invalid_article_params do
    before { @params = valid_article_params.except(:body) }
  end
  
  # or register them at once:  
  share :invalid_article_params,
    lambda { before { @params = valid_article_params.except(:title) } },
    lambda { before { @params = valid_article_params.except(:body) } }

… it would generate two methods and test the actual assertions against both
invalid article params that are missing a title and those that are missing a
body.

Note that the second goal stated above (“run as many assertions with common
preconditions at once as possible”) also means that this does not follow the
one assertion per test
philosophy any more.

E.g. some pseudo code for the first generated method above would be:


  def test_POST_to_create_with_login_as_admin_and_with_valid_article_params
    # preconditions
    @controller.current_user = Admin.new
    @params = {'title' => 'article title', 'body' => 'article body'}
    
    # action
    post :create, @params
    
    # assertions
    it_assigns :article
    it_redirects_to 'articles/1'
  end

(You can still use the default test/unit setup method for setting
up common, expensive scenarios for the whole testcase.)

The reason for this choice is that tests will run much faster when multiple
assertions are validated against the result of one actual controller call (that
goes through routing, controller setup, filters, …) instead of running this
setup for every single assertion.

With this speed gain it is possible to run controller tests without heavy stubbing
and still have reasonable test execution times. So basically the tradeoff I
had in mind with this was: trade “real model usage” instead of heavy stubbing
(which I wanted to get rid of for my controller tests) for multiple assertions
per test (which I think still works fine for controller tests).

Something went wrong with that request. Please try again.