#157 RSpec Matchers & Macros
You can improve the readability and remove duplication in RSpec by adding matchers and macros. Learn how in this episode.
- Download:
- source codeProject Files in Zip (119 KB)
- mp4Full Size H.264 Video (33.3 MB)
- m4vSmaller H.264 Video (21.1 MB)
- webmFull Size VP8 Video (56.9 MB)
- ogvFull Size Theora Video (47 MB)
Resources
Note: Unfortunately there are a couple problems with this episode. Special thanks to David Chelimsky for pointing them out.
- Since RSpec 1.2.0, the method names for failure messages are failure_message_for_should and failure_message_for_should_not. The old method names are still supported, but will be deprecating eventually. The code below has been updated to reflect the new method names.
- Newer versions of RSpec offers a 3rd method for creating a custom matcher. We can apply it to this example and it would have proper error messages. Please see the documentation for details on how this works.
spec_helper.rb
require File.dirname(__FILE__) + "/custom_matchers" require File.dirname(__FILE__) + "/controller_macros" # ... config.include(CustomMatchers) config.include(ControllerMacros, :type => :controller)
spec/custom_matcher.rb
module CustomMatchers class OneMoreThan def initialize(expected) @expected = expected end def matches?(actual) @actual = actual @actual == @expected+1 end def failure_message_for_should "expected #{@actual.inspect} to be one more than #{@expected.inspect}" end def failure_message_for_should_not "expected #{@actual.inspect} not to be one more than #{@expected.inspect}" end end def be_one_more_than(expected) OneMoreThan.new(expected) end end
spec/controller_macros.rb
module ControllerMacros def self.included(base) base.extend(ClassMethods) end module ClassMethods def it_should_require_admin_for_actions(*actions) actions.each do |action| it "#{action} action should require admin" do get action, :id => 1 response.should redirect_to(login_url) flash[:error].should == "Unauthorized Access" end end end end def login_as_admin user = User.new(:username => "admin", :email => "admin@example.com", :password => "secret") user.admin = true user.save! session[:user_id] = user.id end end
article_spec.rb
article2.position.should be_one_more_than(article1.position)
articles_controller_spec.rb
it_should_require_admin_for_actions :new, :create, :edit, :update, :destroy # simple matcher def be_one_more_than(number) simple_matcher("one more than #{number}") { |actual| actual == number+1 } end