From 4aaed186c3997b78ff5be3d4b6ec04f3200ad271 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sun, 10 Jul 2022 17:31:13 -0600 Subject: [PATCH] Add `with` stub modifier --- spec/spectator/dsl/mocks/stub_spec.cr | 45 +++++++++++++++++++++++++ src/spectator/mocks/exception_stub.cr | 6 ++++ src/spectator/mocks/multi_value_stub.cr | 6 ++++ src/spectator/mocks/null_stub.cr | 6 ++++ src/spectator/mocks/proc_stub.cr | 14 ++++++++ src/spectator/mocks/stub_modifiers.cr | 2 ++ src/spectator/mocks/value_stub.cr | 6 ++++ 7 files changed, 85 insertions(+) diff --git a/spec/spectator/dsl/mocks/stub_spec.cr b/spec/spectator/dsl/mocks/stub_spec.cr index 233f667..b911e3b 100644 --- a/spec/spectator/dsl/mocks/stub_spec.cr +++ b/spec/spectator/dsl/mocks/stub_spec.cr @@ -48,4 +48,49 @@ Spectator.describe "Stub DSL", :smoke do expect(captured).to eq(args) end end + + describe "#with" do + context Spectator::MultiValueStub do + it "applies the stub with matching arguments" do + allow(dbl).to receive(:foo).and_return(1, 2, 3).with(Int32, bar: /baz/) + aggregate_failures do + expect(dbl.foo(3, bar: "foobarbaz")).to eq(1) + expect(dbl.foo).to eq(42) + expect(dbl.foo(5, bar: "barbaz")).to eq(2) + expect(dbl.foo(7, bar: "foobaz")).to eq(3) + expect(dbl.foo(11, bar: "baz")).to eq(3) + end + end + end + + context Spectator::NullStub do + it "applies the stub with matching arguments" do + allow(dbl).to receive(:foo).with(Int32, bar: /baz/).and_return(1) + aggregate_failures do + expect(dbl.foo(3, bar: "foobarbaz")).to eq(1) + expect(dbl.foo).to eq(42) + end + end + + it "changes to a proc stub" do + called = 0 + allow(dbl).to receive(:foo).with(Int32, bar: /baz/) { called += 1 } + aggregate_failures do + expect { dbl.foo(3, bar: "foobarbaz") }.to change { called }.from(0).to(1) + expect(dbl.foo(5, bar: "baz")).to eq(2) + expect(dbl.foo).to eq(42) + end + end + end + + context Spectator::ValueStub do + it "applies the stub with matching arguments" do + allow(dbl).to receive(:foo).and_return(1).with(Int32, bar: /baz/) + aggregate_failures do + expect(dbl.foo(3, bar: "foobarbaz")).to eq(1) + expect(dbl.foo).to eq(42) + end + end + end + end end diff --git a/src/spectator/mocks/exception_stub.cr b/src/spectator/mocks/exception_stub.cr index 645b726..7adbd94 100644 --- a/src/spectator/mocks/exception_stub.cr +++ b/src/spectator/mocks/exception_stub.cr @@ -11,6 +11,12 @@ module Spectator raise @exception end + # Returns a new stub with constrained arguments. + def with(*args, **kwargs) + constraint = Arguments.new(args, kwargs) + self.class.new(method, @exception, constraint, location) + end + # Creates the stub. def initialize(method : Symbol, @exception : Exception, constraint : AbstractArguments? = nil, location : Location? = nil) super(method, constraint, location) diff --git a/src/spectator/mocks/multi_value_stub.cr b/src/spectator/mocks/multi_value_stub.cr index 7b797d8..48e5e30 100644 --- a/src/spectator/mocks/multi_value_stub.cr +++ b/src/spectator/mocks/multi_value_stub.cr @@ -15,6 +15,12 @@ module Spectator end end + # Returns a new stub with constrained arguments. + def with(*args, **kwargs) + constraint = Arguments.new(args, kwargs) + self.class.new(method, @values, constraint, location) + end + # Creates the stub. def initialize(method : Symbol, @values : Array(T), constraint : AbstractArguments? = nil, location : Location? = nil) super(method, constraint, location) diff --git a/src/spectator/mocks/null_stub.cr b/src/spectator/mocks/null_stub.cr index df154b6..c365b12 100644 --- a/src/spectator/mocks/null_stub.cr +++ b/src/spectator/mocks/null_stub.cr @@ -7,5 +7,11 @@ module Spectator # Invokes the stubbed implementation. def call(call : MethodCall) : Nil end + + # Returns a new stub with constrained arguments. + def with(*args, **kwargs) + constraint = Arguments.new(args, kwargs) + self.class.new(method, constraint, location) + end end end diff --git a/src/spectator/mocks/proc_stub.cr b/src/spectator/mocks/proc_stub.cr index 1e652b2..4c7d7c5 100644 --- a/src/spectator/mocks/proc_stub.cr +++ b/src/spectator/mocks/proc_stub.cr @@ -10,6 +10,12 @@ module Spectator @proc.call(call.arguments) end + # Returns a new stub with constrained arguments. + def with(*args, **kwargs) + constraint = Arguments.new(args, kwargs) + self.class.new(method, @proc, constraint, location) + end + # Creates the stub. def initialize(method : Symbol, @proc : Proc(AbstractArguments, T), constraint : AbstractArguments? = nil, location : Location? = nil) super(method, constraint, location) @@ -20,4 +26,12 @@ module Spectator initialize(method, block, constraint, location) end end + + module StubModifiers + # Returns a new stub with an argument constraint. + def with(*args, **kwargs, &block : AbstractArguments -> T) forall T + constraint = Arguments.new(args, kwargs) + ProcStub(T).new(method, block, constraint, location) + end + end end diff --git a/src/spectator/mocks/stub_modifiers.cr b/src/spectator/mocks/stub_modifiers.cr index 39b5c18..3f047bc 100644 --- a/src/spectator/mocks/stub_modifiers.cr +++ b/src/spectator/mocks/stub_modifiers.cr @@ -1,5 +1,7 @@ module Spectator # Mixin intended for `Stub` to return new, modified stubs. module StubModifiers + # Returns a new stub of the same type with constrained arguments. + abstract def with(*args, **kwargs) end end diff --git a/src/spectator/mocks/value_stub.cr b/src/spectator/mocks/value_stub.cr index 70aae91..0f7328e 100644 --- a/src/spectator/mocks/value_stub.cr +++ b/src/spectator/mocks/value_stub.cr @@ -11,6 +11,12 @@ module Spectator @value end + # Returns a new stub with constrained arguments. + def with(*args, **kwargs) + constraint = Arguments.new(args, kwargs) + self.class.new(method, @value, constraint, location) + end + # Creates the stub. def initialize(method : Symbol, @value : T, constraint : AbstractArguments? = nil, location : Location? = nil) super(method, constraint, location)