Module Remarkable::ActionController::MacroStubs
In: lib/remarkable_rails/action_controller/macro_stubs.rb

Macro stubs makes stubs and expectations easier, more readable and DRY.

Example

Let‘s jump off to an example:

  describe ProjectsController do
    describe :get => :show, :id => 37 do
      expects :find, :on => Project, :with => '37', :returns => proc { mock_project }

      should_assign_to :project, :with => proc { mock_project }
      should_render_template 'show'

      describe Mime::XML do
        should_assign_to :project
        should_respond_with_content_type Mime::XML
      end
    end
  end

See how the spec is readable: a ProjectsController responding to get show expects :find on Project which a mock project and then should assign to project and render template ‘show’.

Each macro before asserting will check if an action was already performed and if not, it runs the expectations and call the action.

In other words, should assign to macro is basically doing:

  it 'should assign to project' do
    Project.should_receive(:find).with('37').and_return(mock_project)
    get :show, :id => '37'
    assigns(:project).should == mock_project
  end

By default, all macros perform expectations. You can change this behavior sending :with_stubs or :with_expectations as options:

  should_assign_to       :project, :with_stubs => true
  should_render_template 'show', :with_expectations => false

This also works in the rspec way:

  it { should assign_to(:project).with_stubs                   }
  it { should render_template('show').with_expectations(false) }

Attention!

If you need to check that an array is being sent to a method, you need to give an array inside another array, for example:

  expects :comment_ids=, :on => Post, :with => [1,2,3]

Is the same as:

  Post.comment_ids = (1, 2, 3)

And it won‘t work. The right way to handle this is:

  expects :comment_ids=, :on => Post, :with => [[1,2,3]]

mock_models

You don‘t have to play with proc all the time. You can call mock_models which creates two class methods that simply returns a proc and a instance method that do the actual mock.

  describe ProjectsController do
    mock_models :project

And it creates:

  def self.project_proc
    proc { mock_project }
  end

  # To be used on index actions
  def self.projects_proc
    proc { [mock_project] }
  end

  def mock_project(stubs={})
    @project ||= mock_model(Project, stubs)
  end

Then you can replace those lines:

   expects :find, :on => Project, :with => '37', :returns => proc { mock_project }
   should_assign_to :project, :with => proc { mock_project }

For:

   expects :find, :on => Project, :with => '37', :returns => project_proc
   should_assign_to :project, :with => project_proc

Give me more!

If you need to set the example group description, you can also call get, post, put and delete methods:

  describe 'my description' do
    get :show, :id => 37

Things start to get even better when we start to talk about nested resources. After our ProjectsController is created, we want to create a TasksController:

  describe TasksController do
    params :project_id => '42' #=> define params for all requests

    # Those two expectations get inherited in all describe groups below
    expects :find_by_title, :on => Project, :with => '42', :returns => project_proc
    expects :tasks, :and_return => Task

    describe :get => :show, :id => '37' do
      expects :find, :with => '37', :and_return => task_proc

      should_assign_to :project, :task
      should_render_template 'show'
    end
  end

As you noticed, you can define parameters that will be available to all requests, using the method params.

Finally if you need to write a spec by hand, you can invoke the action and expectations with run_action!, run_expectations! and run_stubs!. Examples:

  describe :get => :new do
    expects :new, :on => Project, :returns => project_proc

    it "should do something different" do
      run_action!
      # do you assertions here
    end
  end

Performance!

Remarkable comes with a new way to speed up your tests. It performs the action inside a before(:all), so you can do:

  describe "responding to GET show" do
    get! :show, :id => 37

    should_assign_to :task
    should_render_template :show
  end

Or in the compact way:

  describe :get! => :show, :id => 37

The action will be performed just once before running the macros. If any error happens while performing the action, rspec will output an error but ALL the examples inside the example group (describe) won‘t be run.

By now, the bang methods works only when integrate_views is true and this is when you must see a bigger performance gain.

This feature comes with some rspec and rspec rails tweakings. So if you want to do something before the action is performed (stubs something or log someone in session), you have to do it giving a block to the action method:

  get! :show, :id => 37 do
    login_as(mock_user)
  end

You can still use the compact way and give the block:

  describe :get => :show, :id => 37 do
    get! do
      login_as(mock_user)
    end
  end

Methods

Classes and Modules

Module Remarkable::ActionController::MacroStubs::ClassMethods

Constants

HTTP_VERBS_METHODS = [:get, :get!, :post, :post!, :put, :put!, :delete, :delete!]

Protected Instance methods

Run the action declared in the describe group, but before runs also the expectations. If an action was already performed, it doesn‘t run anything at all and returns false.

The first parameter is if you want to run expectations or stubs. You can also supply the verb (get, post, put or delete), which action to call, parameters, the mime type and if a xhr should be performed. If any of those parameters are supplied, they override the current definition.

[Source]

     # File lib/remarkable_rails/action_controller/macro_stubs.rb, line 541
541:         def run_action!(use_expectations=true, verb=nil, action=nil, params=nil, mime=nil, xhr=nil)
542:           return false if controller.send(:performed?)
543: 
544:           evaluate_expectation_chain(use_expectations)
545: 
546:           mime   ||= default_mime
547:           verb   ||= default_verb
548:           action ||= default_action
549:           params ||= default_params
550:           xhr    ||= default_xhr
551: 
552:           raise ScriptError, "No action was performed or declared." unless verb && action
553: 
554:           request.env["HTTP_ACCEPT"] ||= mime.to_s                if mime
555:           request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' if xhr
556:           send(verb, action, params)
557:         end

Instance method run_expectations! if someone wants to declare additional tests and call the stubs inside of it.

[Source]

     # File lib/remarkable_rails/action_controller/macro_stubs.rb, line 527
527:         def run_expectations!
528:           evaluate_expectation_chain(true)
529:         end

Instance method run_stubs! if someone wants to declare additional tests and call the stubs inside of it.

[Source]

     # File lib/remarkable_rails/action_controller/macro_stubs.rb, line 520
520:         def run_stubs!
521:           evaluate_expectation_chain(false)
522:         end

[Validate]