mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
Add #delegate_method matcher.
This commit is contained in:
parent
c8e3f09ca8
commit
2377fb44af
5 changed files with 315 additions and 0 deletions
9
lib/shoulda/matchers/independent.rb
Normal file
9
lib/shoulda/matchers/independent.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require 'shoulda/matchers/independent/delegate_matcher'
|
||||
|
||||
module Shoulda
|
||||
module Matchers
|
||||
# = Matchers for plain ol' Ruby objects.
|
||||
module Independent
|
||||
end
|
||||
end
|
||||
end
|
123
lib/shoulda/matchers/independent/delegate_matcher.rb
Normal file
123
lib/shoulda/matchers/independent/delegate_matcher.rb
Normal file
|
@ -0,0 +1,123 @@
|
|||
module Shoulda # :nodoc:
|
||||
module Matchers
|
||||
module Independent # :nodoc:
|
||||
|
||||
# Ensure that a given method is delegated properly.
|
||||
#
|
||||
# Basic Syntax:
|
||||
# it { should delegate_method(:deliver_mail).to(:mailman) }
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:as</tt> - tests that the object being delegated to is called with a certain method (defaults to same name as delegating method)
|
||||
# * <tt>:with_arguments</tt> - tests that the method on the object being delegated to is called with certain arguments
|
||||
#
|
||||
# Examples:
|
||||
# it { should delegate_method(:deliver_mail).to(:mailman).as(:deliver_with_haste)
|
||||
# it { should delegate_method(:deliver_mail).to(:mailman).with_arguments("221B Baker St.", speed: :presently)
|
||||
#
|
||||
def delegate_method(delegating_method)
|
||||
require 'bourne'
|
||||
DelegateMatcher.new(delegating_method)
|
||||
rescue LoadError
|
||||
raise "To use Shoulda's #delegate_method matcher, please add `bourne` to your Gemfile."
|
||||
end
|
||||
|
||||
class DelegateMatcher
|
||||
def initialize(delegating_method)
|
||||
@delegating_method = delegating_method
|
||||
end
|
||||
|
||||
def matches?(subject)
|
||||
@subject = subject
|
||||
ensure_target_method_is_present!
|
||||
|
||||
begin
|
||||
extend Mocha::API
|
||||
|
||||
stubbed_object = stub(method_on_target)
|
||||
subject.stubs(@target_method).returns(stubbed_object)
|
||||
subject.send(@delegating_method)
|
||||
|
||||
stubbed_object.should have_received(method_on_target).with(*@delegated_arguments)
|
||||
rescue NoMethodError, RSpec::Expectations::ExpectationNotMetError, Mocha::ExpectationError
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def does_not_match?(subject)
|
||||
raise InvalidDelegateMatcher
|
||||
end
|
||||
|
||||
def to(target_method)
|
||||
@target_method = target_method
|
||||
self
|
||||
end
|
||||
|
||||
def as(method_on_target)
|
||||
@method_on_target = method_on_target
|
||||
self
|
||||
end
|
||||
|
||||
def with_arguments(*arguments)
|
||||
@delegated_arguments = arguments
|
||||
self
|
||||
end
|
||||
|
||||
def failure_message
|
||||
base = "Expected #{delegating_method_name} to delegate to #{target_method_name}"
|
||||
add_clarifications_to(base)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_clarifications_to(message)
|
||||
message.tap do |message|
|
||||
if @delegated_arguments.present?
|
||||
message.concat(" with arguments: #{@delegated_arguments}")
|
||||
end
|
||||
|
||||
if @method_on_target.present?
|
||||
message.concat(" as :#{@method_on_target}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def delegating_method_name
|
||||
method_name_with_class(@delegating_method)
|
||||
end
|
||||
|
||||
def target_method_name
|
||||
method_name_with_class(@target_method)
|
||||
end
|
||||
|
||||
def method_name_with_class(method)
|
||||
if Class === @subject
|
||||
@subject.name + "." + method.to_s
|
||||
else
|
||||
@subject.class.name + "#" + method.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def method_on_target
|
||||
@method_on_target || @delegating_method
|
||||
end
|
||||
|
||||
def ensure_target_method_is_present!
|
||||
raise TargetNotDefinedError unless @target_method.present?
|
||||
end
|
||||
end
|
||||
|
||||
class DelegateMatcher::TargetNotDefinedError < StandardError
|
||||
def message
|
||||
"Delegation needs a target. Use the #to method to define one, e.g. `post_office.should delegate(:deliver_mail).to(:mailman)`"
|
||||
end
|
||||
end
|
||||
|
||||
class DelegateMatcher::InvalidDelegateMatcher < StandardError
|
||||
def message
|
||||
"#delegate_to does not support #should_not syntax."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,10 @@
|
|||
# :enddoc:
|
||||
|
||||
require 'shoulda/matchers/independent'
|
||||
module RSpec::Matchers
|
||||
include Shoulda::Matchers::Independent
|
||||
end
|
||||
|
||||
if defined?(::ActiveRecord)
|
||||
require 'shoulda/matchers/active_record'
|
||||
require 'shoulda/matchers/active_model'
|
||||
|
|
|
@ -1,4 +1,15 @@
|
|||
# :enddoc:
|
||||
require 'test/unit/testcase'
|
||||
|
||||
require 'shoulda/matchers/independent'
|
||||
module Test
|
||||
module Unit
|
||||
class TestCase
|
||||
include Shoulda::Matchers::Independent
|
||||
extend Shoulda::Matchers::Independent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if defined?(ActionController)
|
||||
require 'shoulda/matchers/action_controller'
|
||||
|
|
167
spec/shoulda/independent/delegate_matcher_spec.rb
Normal file
167
spec/shoulda/independent/delegate_matcher_spec.rb
Normal file
|
@ -0,0 +1,167 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Shoulda::Matchers::Independent::DelegateMatcher do
|
||||
it 'supports chaining on #to' do
|
||||
matcher = delegate_method(:method)
|
||||
matcher.to(:another_method).should == matcher
|
||||
end
|
||||
|
||||
it 'supports chaining on #with_arguments' do
|
||||
matcher = delegate_method(:method)
|
||||
matcher.with_arguments(1, 2, 3).should == matcher
|
||||
end
|
||||
|
||||
it 'supports chaining on #as' do
|
||||
matcher = delegate_method(:method)
|
||||
matcher.as(:some_other_method).should == matcher
|
||||
end
|
||||
|
||||
it 'should raise an error if no delegation target is defined' do
|
||||
object = Object.new
|
||||
expect {
|
||||
object.should delegate_method(:name)
|
||||
}.to raise_exception Shoulda::Matchers::Independent::DelegateMatcher::TargetNotDefinedError
|
||||
end
|
||||
|
||||
it 'should raise an error if called with #should_not' do
|
||||
object = Object.new
|
||||
expect {
|
||||
object.should_not delegate_method(:name).to(:anyone)
|
||||
}.to raise_exception Shoulda::Matchers::Independent::DelegateMatcher::InvalidDelegateMatcher
|
||||
end
|
||||
|
||||
context 'given a method that does not delegate' do
|
||||
before do
|
||||
class PostOffice
|
||||
def deliver_mail
|
||||
:delivered
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'fails with a useful message' do
|
||||
begin
|
||||
post_office = PostOffice.new
|
||||
post_office.should delegate_method(:deliver_mail).to(:mailman)
|
||||
rescue Exception => e
|
||||
e.message.should == 'Expected PostOffice#deliver_mail to delegate to PostOffice#mailman'
|
||||
end
|
||||
end
|
||||
|
||||
it 'uses the proper syntax for class methods in errors' do
|
||||
begin
|
||||
PostOffice.should delegate_method(:deliver_mail).to(:mailman)
|
||||
rescue => e
|
||||
e.message.should == 'Expected PostOffice.deliver_mail to delegate to PostOffice.mailman'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a method that delegates properly' do
|
||||
before do
|
||||
class Mailman; end
|
||||
class PostOffice
|
||||
def deliver_mail
|
||||
mailman.deliver_mail
|
||||
end
|
||||
|
||||
def mailman
|
||||
Mailman.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'succeeds' do
|
||||
post_office = PostOffice.new
|
||||
post_office.should delegate_method(:deliver_mail).to(:mailman)
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a method that delegates properly with certain arguments' do
|
||||
before do
|
||||
class Mailman; end
|
||||
class PostOffice
|
||||
def deliver_mail
|
||||
mailman.deliver_mail("221B Baker St.", speed: :presently)
|
||||
end
|
||||
|
||||
def mailman
|
||||
Mailman.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given the correct arguments' do
|
||||
it 'succeeds' do
|
||||
post_office = PostOffice.new
|
||||
post_office.should delegate_method(:deliver_mail)
|
||||
.to(:mailman)
|
||||
.with_arguments("221B Baker St.", speed: :presently)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not given the correct arguments' do
|
||||
it 'fails with a useful message' do
|
||||
begin
|
||||
post_office = PostOffice.new
|
||||
post_office.should delegate_method(:deliver_mail)
|
||||
.to(:mailman)
|
||||
.with_arguments("123 Nowhere Ln.")
|
||||
rescue Exception => e
|
||||
e.message.should == 'Expected PostOffice#deliver_mail to delegate to PostOffice#mailman with arguments: ["123 Nowhere Ln."]'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a method that delegates properly to a method of a different name' do
|
||||
before do
|
||||
class Mailman; end
|
||||
class PostOffice
|
||||
def deliver_mail
|
||||
mailman.deliver_mail_and_avoid_dogs
|
||||
end
|
||||
|
||||
def mailman
|
||||
Mailman.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given the correct method name' do
|
||||
it 'succeeds' do
|
||||
post_office = PostOffice.new
|
||||
post_office.should delegate_method(:deliver_mail)
|
||||
.to(:mailman)
|
||||
.as(:deliver_mail_and_avoid_dogs)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given an incorrect method name' do
|
||||
it 'fails with a useful message' do
|
||||
begin
|
||||
post_office = PostOffice.new
|
||||
post_office.should delegate_method(:deliver_mail)
|
||||
.to(:mailman)
|
||||
.as(:deliver_mail_without_regard_for_safety)
|
||||
rescue Exception => e
|
||||
e.message.should == "Expected PostOffice#deliver_mail to delegate to PostOffice#mailman as :deliver_mail_without_regard_for_safety"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Shoulda::Matchers::Independent::DelegateMatcher::TargetNotDefinedError do
|
||||
it 'has a useful message' do
|
||||
error = Shoulda::Matchers::Independent::DelegateMatcher::TargetNotDefinedError.new
|
||||
error.message.should include "Delegation needs a target."
|
||||
end
|
||||
end
|
||||
|
||||
describe Shoulda::Matchers::Independent::DelegateMatcher::InvalidDelegateMatcher do
|
||||
it 'has a useful message' do
|
||||
error = Shoulda::Matchers::Independent::DelegateMatcher::InvalidDelegateMatcher.new
|
||||
error.message.should include "does not support #should_not"
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue