Module: Remarkable::ActionController::MacroStubs
- Defined in:
- lib/remarkable_rails/action_controller/macro_stubs.rb
Overview
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
Defined Under Namespace
Modules: ClassMethods
Constant Summary collapse
- HTTP_VERBS_METHODS =
[:get, :get!, :post, :post!, :put, :put!, :delete, :delete!]
Class Method Summary collapse
-
.included(base) ⇒ Object
:nodoc:.
Class Method Details
.included(base) ⇒ Object
:nodoc:
182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/remarkable_rails/action_controller/macro_stubs.rb', line 182 def self.included(base) #:nodoc: base.extend ClassMethods base.class_inheritable_reader :expects_chain, :default_action, :default_mime, :default_verb, :default_params, :default_xhr, :before_all_block base.class_eval do class << self alias_method :context, :describe end end end |