mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
Doublespeak: Introduce a better MethodCall
MethodCall is used to represent a method call made on a double. It stores the object the call was made on, the name of the method, any arguments or block sent to the method, and the double itself. There were quite a few places where we were using an (args, block) or (object, args, block) tuple. We also already had a MethodCall class, and additionally a MethodCallWithName class, and the two were basically the same. These usages have all been collapsed.
This commit is contained in:
parent
878cfbd4cc
commit
5e67a8305b
11 changed files with 123 additions and 71 deletions
|
@ -215,7 +215,7 @@ module Shoulda
|
|||
@double_collection =
|
||||
Doublespeak.double_collection_for(::ActionController::Parameters)
|
||||
|
||||
@double_collection.register_stub(:require).to_return { |params| params }
|
||||
@double_collection.register_stub(:require).to_return(&:object)
|
||||
@double_collection.register_proxy(:permit)
|
||||
end
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ end
|
|||
require 'shoulda/matchers/doublespeak/double'
|
||||
require 'shoulda/matchers/doublespeak/double_collection'
|
||||
require 'shoulda/matchers/doublespeak/double_implementation_registry'
|
||||
require 'shoulda/matchers/doublespeak/method_call'
|
||||
require 'shoulda/matchers/doublespeak/object_double'
|
||||
require 'shoulda/matchers/doublespeak/proxy_implementation'
|
||||
require 'shoulda/matchers/doublespeak/structs'
|
||||
require 'shoulda/matchers/doublespeak/stub_implementation'
|
||||
require 'shoulda/matchers/doublespeak/world'
|
||||
|
|
|
@ -36,13 +36,13 @@ module Shoulda
|
|||
end
|
||||
end
|
||||
|
||||
def record_call(args, block)
|
||||
calls << MethodCall.new(args, block)
|
||||
def record_call(call)
|
||||
calls << call
|
||||
end
|
||||
|
||||
def call_original_method(object, args, block)
|
||||
def call_original_method(call)
|
||||
if original_method
|
||||
original_method.bind(object).call(*args, &block)
|
||||
original_method.bind(call.object).call(*call.args, &call.block)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -55,11 +55,19 @@ module Shoulda
|
|||
end
|
||||
|
||||
def replace_method_with_double
|
||||
implementation = @implementation
|
||||
double = self
|
||||
implementation = @implementation
|
||||
_method_name = method_name
|
||||
|
||||
klass.__send__(:define_method, method_name) do |*args, &block|
|
||||
implementation.call(double, self, args, block)
|
||||
call = MethodCall.new(
|
||||
double: double,
|
||||
object: self,
|
||||
method_name: _method_name,
|
||||
args: args,
|
||||
block: block
|
||||
)
|
||||
implementation.call(call)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
26
lib/shoulda/matchers/doublespeak/method_call.rb
Normal file
26
lib/shoulda/matchers/doublespeak/method_call.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module Doublespeak
|
||||
class MethodCall
|
||||
attr_reader :method_name, :args, :block, :object, :double
|
||||
|
||||
def initialize(args)
|
||||
@method_name = args.fetch(:method_name)
|
||||
@args = args.fetch(:args)
|
||||
@block = args[:block]
|
||||
@double = args[:double]
|
||||
@object = args[:object]
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(self.class) &&
|
||||
method_name == other.method_name &&
|
||||
args == other.args &&
|
||||
block == other.block &&
|
||||
double == other.double &&
|
||||
object == other.object
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,8 +19,13 @@ module Shoulda
|
|||
end
|
||||
|
||||
def method_missing(method_name, *args, &block)
|
||||
calls << MethodCallWithName.new(method_name, args, block)
|
||||
(calls_by_method_name[method_name] ||= []) << MethodCall.new(args, block)
|
||||
call = MethodCall.new(
|
||||
method_name: method_name,
|
||||
args: args,
|
||||
block: block
|
||||
)
|
||||
calls << call
|
||||
(calls_by_method_name[method_name] ||= []) << call
|
||||
nil
|
||||
end
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ module Shoulda
|
|||
@stub_implementation = stub_implementation
|
||||
end
|
||||
|
||||
def call(double, object, args, block)
|
||||
stub_implementation.call(double, object, args, block)
|
||||
double.call_original_method(object, args, block)
|
||||
def call(call)
|
||||
stub_implementation.call(call)
|
||||
call.double.call_original_method(call)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module Doublespeak
|
||||
# @private
|
||||
MethodCall = Struct.new(:args, :block)
|
||||
# @private
|
||||
MethodCallWithName = Struct.new(:method_name, :args, :block)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,9 +21,9 @@ module Shoulda
|
|||
end
|
||||
end
|
||||
|
||||
def call(double, object, args, block)
|
||||
double.record_call(args, block)
|
||||
implementation.call(object, args, block)
|
||||
def call(call)
|
||||
call.double.record_call(call)
|
||||
implementation.call(call)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -41,18 +41,26 @@ module Shoulda::Matchers::Doublespeak
|
|||
describe '#activate' do
|
||||
it 'replaces the method with an implementation' do
|
||||
implementation = build_implementation
|
||||
klass = create_class(a_method: 42)
|
||||
method_name = :a_method
|
||||
klass = create_class(method_name => :some_return_value)
|
||||
instance = klass.new
|
||||
double = described_class.new(klass, :a_method, implementation)
|
||||
double = described_class.new(klass, method_name, implementation)
|
||||
args = [:any, :args]
|
||||
block = -> {}
|
||||
call = MethodCall.new(
|
||||
double: double,
|
||||
object: instance,
|
||||
method_name: method_name,
|
||||
args: args,
|
||||
block: block
|
||||
)
|
||||
|
||||
double.activate
|
||||
instance.a_method(*args, &block)
|
||||
instance.__send__(method_name, *args, &block)
|
||||
|
||||
expect(implementation).
|
||||
to have_received(:call).
|
||||
with(double, instance, args, block)
|
||||
with(call)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -92,29 +100,26 @@ module Shoulda::Matchers::Doublespeak
|
|||
end
|
||||
|
||||
describe '#record_call' do
|
||||
it 'stores the arguments and block given to the method in calls' do
|
||||
double = described_class.new(:klass, :a_method, :implementation)
|
||||
calls = [
|
||||
[:any, :args], :block,
|
||||
[:more, :args]
|
||||
]
|
||||
double.record_call(calls[0][0], calls[0][1])
|
||||
double.record_call(calls[1][0], nil)
|
||||
|
||||
expect(double.calls[0].args).to eq calls[0][0]
|
||||
expect(double.calls[0].block).to eq calls[0][1]
|
||||
expect(double.calls[1].args).to eq calls[1][0]
|
||||
it 'adds the given call to the list of calls' do
|
||||
double = described_class.new(:a_klass, :a_method, :an_implementation)
|
||||
double.record_call(:some_call)
|
||||
expect(double.calls.last).to eq :some_call
|
||||
end
|
||||
end
|
||||
|
||||
describe '#call_original_method' do
|
||||
it 'binds the stored method object to the class and calls it with the given args and block' do
|
||||
it 'binds the stored method object to the given object and calls it with the given args and block' do
|
||||
klass = create_class
|
||||
instance = klass.new
|
||||
actual_args = actual_block = method_called = nil
|
||||
expected_args = [:one, :two, :three]
|
||||
expected_block = -> { }
|
||||
double = described_class.new(klass, :a_method, :implementation)
|
||||
call = double('call',
|
||||
object: instance,
|
||||
args: expected_args,
|
||||
block: expected_block
|
||||
)
|
||||
double = described_class.new(klass, :a_method, :an_implementation)
|
||||
|
||||
klass.__send__(:define_method, :a_method) do |*args, &block|
|
||||
actual_args = expected_args
|
||||
|
@ -123,7 +128,7 @@ module Shoulda::Matchers::Doublespeak
|
|||
end
|
||||
|
||||
double.activate
|
||||
double.call_original_method(instance, expected_args, expected_block)
|
||||
double.call_original_method(call)
|
||||
|
||||
expect(expected_args).to eq actual_args
|
||||
expect(expected_block).to eq actual_block
|
||||
|
@ -131,11 +136,8 @@ module Shoulda::Matchers::Doublespeak
|
|||
end
|
||||
|
||||
it 'does nothing if no method has been stored' do
|
||||
double = described_class.new(:klass, :a_method, :implementation)
|
||||
|
||||
expect {
|
||||
double.call_original_method(:instance, [:any, :args], nil)
|
||||
}.not_to raise_error
|
||||
double = described_class.new(:klass, :a_method, :an_implementation)
|
||||
expect { double.call_original_method(:a_call) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -15,24 +15,28 @@ module Shoulda::Matchers::Doublespeak
|
|||
describe '#call' do
|
||||
it 'delegates to its stub_implementation' do
|
||||
stub_implementation = build_stub_implementation
|
||||
double = build_double
|
||||
call = build_call
|
||||
implementation = described_class.new(stub_implementation)
|
||||
implementation.call(double, :object, :args, :block)
|
||||
|
||||
implementation.call(call)
|
||||
|
||||
expect(stub_implementation).
|
||||
to have_received(:call).
|
||||
with(double, :object, :args, :block)
|
||||
with(call)
|
||||
end
|
||||
|
||||
it 'calls #call_original_method on the double' do
|
||||
stub_implementation = build_stub_implementation
|
||||
implementation = described_class.new(stub_implementation)
|
||||
double = build_double
|
||||
implementation.call(double, :object, :args, :block)
|
||||
call = build_call(double: double)
|
||||
allow(double).to receive(:call_original_method).and_return(call)
|
||||
implementation = described_class.new(stub_implementation)
|
||||
|
||||
implementation.call(call)
|
||||
|
||||
expect(double).
|
||||
to have_received(:call_original_method).
|
||||
with(:object, :args, :block)
|
||||
with(call)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,5 +47,9 @@ module Shoulda::Matchers::Doublespeak
|
|||
def build_double
|
||||
double('double', call_original_method: nil)
|
||||
end
|
||||
|
||||
def build_call(double: build_double)
|
||||
double('call', double: double)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,18 +6,20 @@ module Shoulda::Matchers::Doublespeak
|
|||
it 'calls #record_call on the double' do
|
||||
implementation = described_class.new
|
||||
double = build_double
|
||||
call = build_call(double: double)
|
||||
|
||||
allow(double).to receive(:record_call).with(:args, :block)
|
||||
allow(double).to receive(:record_call).with(call)
|
||||
|
||||
implementation.call(double, :object, :args, :block)
|
||||
implementation.call(call)
|
||||
end
|
||||
|
||||
context 'if no explicit implementation was set' do
|
||||
it 'returns nil' do
|
||||
implementation = described_class.new
|
||||
double = build_double
|
||||
call = build_call(double: double)
|
||||
|
||||
return_value = implementation.call(double, :object, :args, :block)
|
||||
return_value = implementation.call(call)
|
||||
|
||||
expect(return_value).to eq nil
|
||||
end
|
||||
|
@ -28,29 +30,33 @@ module Shoulda::Matchers::Doublespeak
|
|||
implementation = described_class.new
|
||||
implementation.returns(42)
|
||||
double = build_double
|
||||
call = build_call(double: double)
|
||||
|
||||
return_value = implementation.call(double, :object, :args, :block)
|
||||
return_value = implementation.call(call)
|
||||
|
||||
expect(return_value).to eq 42
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the implementation was set as a block' do
|
||||
it 'calls the block with the object and args/block passed to the method' do
|
||||
it 'calls the block with the MethodCall object the implementation was called with' do
|
||||
double = build_double
|
||||
expected_object, expected_args, expected_block = :object, :args, :block
|
||||
call = build_call(
|
||||
double: double,
|
||||
object: expected_object,
|
||||
args: expected_args,
|
||||
block: expected_block
|
||||
)
|
||||
actual_object, actual_args, actual_block = []
|
||||
implementation = described_class.new
|
||||
implementation.returns do |object, args, block|
|
||||
actual_object, actual_args, actual_block = object, args, block
|
||||
implementation.returns do |actual_call|
|
||||
actual_object = actual_call.object
|
||||
actual_args = actual_call.args
|
||||
actual_block = actual_call.block
|
||||
end
|
||||
|
||||
implementation.call(
|
||||
double,
|
||||
expected_object,
|
||||
expected_args,
|
||||
expected_block
|
||||
)
|
||||
implementation.call(call)
|
||||
|
||||
expect(actual_object).to eq expected_object
|
||||
expect(actual_args).to eq expected_args
|
||||
|
@ -61,8 +67,9 @@ module Shoulda::Matchers::Doublespeak
|
|||
implementation = described_class.new
|
||||
implementation.returns { 42 }
|
||||
double = build_double
|
||||
call = build_call(double: double)
|
||||
|
||||
return_value = implementation.call(double, :object, :args, :block)
|
||||
return_value = implementation.call(call)
|
||||
|
||||
expect(return_value).to eq 42
|
||||
end
|
||||
|
@ -73,8 +80,9 @@ module Shoulda::Matchers::Doublespeak
|
|||
implementation = described_class.new
|
||||
implementation.returns(:something_else) { 42 }
|
||||
double = build_double
|
||||
call = build_call(double: double)
|
||||
|
||||
return_value = implementation.call(double, :object, :args, :block)
|
||||
return_value = implementation.call(call)
|
||||
|
||||
expect(return_value).to eq 42
|
||||
end
|
||||
|
@ -84,5 +92,10 @@ module Shoulda::Matchers::Doublespeak
|
|||
def build_double
|
||||
double('double', record_call: nil)
|
||||
end
|
||||
|
||||
def build_call(options = {})
|
||||
defaults = { double: build_double }
|
||||
double('call', defaults.merge(options))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue