2015-02-28 22:28:56 -05:00
|
|
|
require 'doublespeak_spec_helper'
|
2014-04-19 20:01:22 -04:00
|
|
|
|
|
|
|
module Shoulda::Matchers::Doublespeak
|
|
|
|
describe Double do
|
Fix #permit so #on works and is properly tested
Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
2015-09-28 18:48:14 -04:00
|
|
|
describe 'initializer' do
|
|
|
|
context 'if doubles are currently activated on the world level' do
|
|
|
|
it 'immediately activates the new Double' do
|
|
|
|
world = build_world(doubles_activated?: true)
|
|
|
|
klass = create_class(a_method_name: nil)
|
|
|
|
implementation = build_implementation
|
|
|
|
|
|
|
|
double = described_class.new(world, klass, :a_method_name, implementation)
|
|
|
|
|
|
|
|
expect(double).to be_activated
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-19 20:01:22 -04:00
|
|
|
describe '#to_return' do
|
|
|
|
it 'tells its implementation to call the given block' do
|
|
|
|
sent_block = -> { }
|
|
|
|
actual_block = nil
|
2014-11-07 17:22:20 -05:00
|
|
|
implementation = build_implementation
|
|
|
|
implementation.singleton_class.__send__(:undef_method, :returns)
|
2014-04-19 20:01:22 -04:00
|
|
|
implementation.singleton_class.__send__(:define_method, :returns) do |&block|
|
|
|
|
actual_block = block
|
|
|
|
end
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
Fix #permit so #on works and is properly tested
Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
2015-09-28 18:48:14 -04:00
|
|
|
build_world,
|
2015-03-01 00:03:49 -05:00
|
|
|
:klass,
|
|
|
|
:a_method,
|
|
|
|
implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
double.to_return(&sent_block)
|
|
|
|
expect(actual_block).to eq sent_block
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'tells its implementation to return the given value' do
|
2014-11-07 17:22:20 -05:00
|
|
|
implementation = build_implementation
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
Fix #permit so #on works and is properly tested
Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
2015-09-28 18:48:14 -04:00
|
|
|
build_world,
|
2015-03-01 00:03:49 -05:00
|
|
|
:klass,
|
|
|
|
:a_method,
|
|
|
|
implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
double.to_return(:implementation)
|
2014-11-07 17:22:20 -05:00
|
|
|
|
|
|
|
expect(implementation).to have_received(:returns).with(:implementation)
|
2014-04-19 20:01:22 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'prefers a block over a non-block' do
|
|
|
|
sent_block = -> { }
|
|
|
|
actual_block = nil
|
2014-11-07 17:22:20 -05:00
|
|
|
implementation = build_implementation
|
|
|
|
implementation.singleton_class.__send__(:undef_method, :returns)
|
2014-04-19 20:01:22 -04:00
|
|
|
implementation.singleton_class.__send__(:define_method, :returns) do |&block|
|
|
|
|
actual_block = block
|
|
|
|
end
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
Fix #permit so #on works and is properly tested
Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
2015-09-28 18:48:14 -04:00
|
|
|
build_world,
|
2015-03-01 00:03:49 -05:00
|
|
|
:klass,
|
|
|
|
:a_method,
|
|
|
|
implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
double.to_return(:value, &sent_block)
|
|
|
|
expect(actual_block).to eq sent_block
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#activate' do
|
|
|
|
it 'replaces the method with an implementation' do
|
2014-11-07 17:22:20 -05:00
|
|
|
implementation = build_implementation
|
2015-02-28 22:47:39 -05:00
|
|
|
method_name = :a_method
|
|
|
|
klass = create_class(method_name => :some_return_value)
|
2014-04-19 20:01:22 -04:00
|
|
|
instance = klass.new
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
|
|
|
build_world,
|
|
|
|
klass,
|
|
|
|
method_name,
|
|
|
|
implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
args = [:any, :args]
|
|
|
|
block = -> {}
|
2015-02-28 22:47:39 -05:00
|
|
|
call = MethodCall.new(
|
|
|
|
double: double,
|
|
|
|
object: instance,
|
|
|
|
method_name: method_name,
|
|
|
|
args: args,
|
2015-09-29 12:48:55 -04:00
|
|
|
block: block,
|
|
|
|
caller: :some_caller
|
2015-02-28 22:47:39 -05:00
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
|
|
|
|
double.activate
|
2015-02-28 22:47:39 -05:00
|
|
|
instance.__send__(method_name, *args, &block)
|
2014-11-07 17:22:20 -05:00
|
|
|
|
|
|
|
expect(implementation).
|
|
|
|
to have_received(:call).
|
2015-02-28 22:47:39 -05:00
|
|
|
with(call)
|
2014-04-19 20:01:22 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#deactivate' do
|
|
|
|
it 'restores the original method after being doubled' do
|
|
|
|
klass = create_class(a_method: 42)
|
2015-03-01 00:03:49 -05:00
|
|
|
world = build_world(
|
|
|
|
original_method_for: klass.instance_method(:a_method)
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
instance = klass.new
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
|
|
|
world,
|
|
|
|
klass,
|
|
|
|
:a_method,
|
|
|
|
build_implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
|
|
|
|
double.activate
|
|
|
|
double.deactivate
|
|
|
|
expect(instance.a_method).to eq 42
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'still restores the original method if #activate was called twice' do
|
2015-03-01 00:03:49 -05:00
|
|
|
method_name = :a_method
|
|
|
|
klass = create_class(method_name => 42)
|
|
|
|
world = build_world(
|
|
|
|
original_method_for: klass.instance_method(:a_method)
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
instance = klass.new
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
|
|
|
world,
|
|
|
|
klass,
|
|
|
|
:a_method,
|
|
|
|
build_implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
|
|
|
|
double.activate
|
|
|
|
double.activate
|
|
|
|
double.deactivate
|
|
|
|
expect(instance.a_method).to eq 42
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does nothing if the method has not been doubled' do
|
|
|
|
klass = create_class(a_method: 42)
|
|
|
|
instance = klass.new
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
Fix #permit so #on works and is properly tested
Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
2015-09-28 18:48:14 -04:00
|
|
|
build_world,
|
2015-03-01 00:03:49 -05:00
|
|
|
klass,
|
|
|
|
:a_method,
|
|
|
|
build_implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
|
|
|
|
double.deactivate
|
|
|
|
expect(instance.a_method).to eq 42
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#record_call' do
|
2015-02-28 22:47:39 -05:00
|
|
|
it 'adds the given call to the list of calls' do
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
Fix #permit so #on works and is properly tested
Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
2015-09-28 18:48:14 -04:00
|
|
|
build_world,
|
2015-03-01 00:03:49 -05:00
|
|
|
:a_klass,
|
|
|
|
:a_method,
|
|
|
|
:an_implementation
|
|
|
|
)
|
2015-02-28 22:47:39 -05:00
|
|
|
double.record_call(:some_call)
|
|
|
|
expect(double.calls.last).to eq :some_call
|
2014-04-19 20:01:22 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#call_original_method' do
|
2015-02-28 22:47:39 -05:00
|
|
|
it 'binds the stored method object to the given object and calls it with the given args and block' do
|
2014-04-19 20:01:22 -04:00
|
|
|
expected_args = [:one, :two, :three]
|
|
|
|
expected_block = -> { }
|
2015-03-01 00:03:49 -05:00
|
|
|
actual_args = actual_block = method_called = nil
|
|
|
|
method_name = :a_method
|
|
|
|
klass = create_class
|
|
|
|
klass.__send__(:define_method, method_name) do |*args, &block|
|
|
|
|
actual_args = args
|
|
|
|
actual_block = block
|
|
|
|
method_called = true
|
|
|
|
end
|
|
|
|
world = build_world(
|
|
|
|
original_method_for: klass.instance_method(method_name)
|
|
|
|
)
|
|
|
|
instance = klass.new
|
2015-02-28 22:47:39 -05:00
|
|
|
call = double('call',
|
|
|
|
object: instance,
|
2015-03-01 00:03:49 -05:00
|
|
|
method_name: method_name,
|
2015-02-28 22:47:39 -05:00
|
|
|
args: expected_args,
|
|
|
|
block: expected_block
|
|
|
|
)
|
2015-03-01 00:03:49 -05:00
|
|
|
double = described_class.new(
|
|
|
|
world,
|
|
|
|
klass,
|
|
|
|
method_name,
|
|
|
|
:an_implementation
|
|
|
|
)
|
2014-04-19 20:01:22 -04:00
|
|
|
|
|
|
|
double.activate
|
2015-02-28 22:47:39 -05:00
|
|
|
double.call_original_method(call)
|
2014-04-19 20:01:22 -04:00
|
|
|
|
2015-03-01 00:03:49 -05:00
|
|
|
expect(actual_args).to eq expected_args
|
|
|
|
expect(actual_block).to eq expected_block
|
2014-04-19 20:01:22 -04:00
|
|
|
expect(method_called).to eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does nothing if no method has been stored' do
|
2015-03-01 00:03:49 -05:00
|
|
|
method_name = :a_method
|
|
|
|
world = build_world(original_method_for: nil)
|
|
|
|
call = double('call', method_name: method_name)
|
|
|
|
double = described_class.new(
|
|
|
|
world,
|
|
|
|
:klass,
|
|
|
|
method_name,
|
|
|
|
:an_implementation
|
|
|
|
)
|
|
|
|
expect { double.call_original_method(call) }.not_to raise_error
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not store the original method multiple times when a method is doubled multiple times' do
|
|
|
|
world = Shoulda::Matchers::Doublespeak::World.new
|
|
|
|
klass = create_class(a_method: :some_return_value)
|
|
|
|
method_name = :a_method
|
|
|
|
doubles = 2.times.map do
|
|
|
|
described_class.new(
|
|
|
|
world,
|
|
|
|
klass,
|
|
|
|
method_name,
|
|
|
|
build_implementation
|
|
|
|
)
|
|
|
|
end
|
|
|
|
instance = klass.new
|
|
|
|
|
|
|
|
doubles[0].activate
|
|
|
|
|
|
|
|
was_called = false
|
|
|
|
klass.__send__(:define_method, method_name) do
|
|
|
|
was_called = true
|
|
|
|
end
|
|
|
|
|
|
|
|
doubles[1].activate
|
|
|
|
|
|
|
|
doubles.each(&:deactivate)
|
|
|
|
|
|
|
|
instance.__send__(method_name)
|
|
|
|
|
|
|
|
expect(was_called).to be false
|
2014-04-19 20:01:22 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_class(methods = {})
|
|
|
|
Class.new.tap do |klass|
|
|
|
|
methods.each do |name, value|
|
|
|
|
klass.__send__(:define_method, name) { |*args| value }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-11-07 17:22:20 -05:00
|
|
|
|
|
|
|
def build_implementation
|
|
|
|
double('implementation', returns: nil, call: nil)
|
|
|
|
end
|
2015-03-01 00:03:49 -05:00
|
|
|
|
|
|
|
def build_world(methods = {})
|
|
|
|
defaults = {
|
|
|
|
original_method_for: nil,
|
Fix #permit so #on works and is properly tested
Why:
* When using #permit with the #on qualifier to assert that #permit was
called on a subset of the params hash (selected using #require), the
matcher would fail. The matcher didn't properly detect that #permit
was called on the slice -- it thought it was called on the parent
params object.
* It turns out this was a bug in Doublespeak. When #require is called,
we take the subset of the params, which is also an
ActionController::Parameters object like params, and we stub #permit
on it at runtime. Unfortunately the Double object created here was
never activated, so when #permit was called, this Double wasn't the
one run. Instead, the Double on #permit within the
ActionController::Parameters class (which is stubbed at the beginning)
was the one that was run, and it's this object that recorded the call
on #permit incorrectly.
* This bug slipped through because it turns out when #on was added it
wasn't tested very well.
To satisfy the above:
* Modify Doublespeak so that when it is in activated mode, whenever
new doubles are created, activate them immediately.
* Fix all of the tests for #permit around operating on a slice of the
params hash so that they use the #on qualifier.
2015-09-28 18:48:14 -04:00
|
|
|
store_original_method_for: nil,
|
|
|
|
doubles_activated?: nil
|
2015-03-01 00:03:49 -05:00
|
|
|
}
|
|
|
|
double('world', defaults.merge(methods))
|
|
|
|
end
|
2014-04-19 20:01:22 -04:00
|
|
|
end
|
|
|
|
end
|