From 6e57a1c44ac4d78e06462b96243e1d498ce6bef3 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Tue, 12 Jul 2022 19:23:13 -0600 Subject: [PATCH] Allow method calls with unconstrained arguments Workaround for the expect-receive DSL syntax to allow methods to be called without matching arguments. --- .../dsl/mocks/expect_receive_spec.cr | 13 +++++++++- src/spectator/expectation.cr | 26 +++++++++++++++++++ src/spectator/mocks/double.cr | 2 +- src/spectator/mocks/mocked.cr | 2 +- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/spec/spectator/dsl/mocks/expect_receive_spec.cr b/spec/spectator/dsl/mocks/expect_receive_spec.cr index 51c0b7c..d4cb7c5 100644 --- a/spec/spectator/dsl/mocks/expect_receive_spec.cr +++ b/spec/spectator/dsl/mocks/expect_receive_spec.cr @@ -6,6 +6,7 @@ Spectator.describe "Deferred stub expectation DSL" do # Ensure the original is never called. stub abstract def foo : Nil stub abstract def foo(arg) : Nil + stub abstract def value : Int32 end let(dbl) { double(:dbl) } @@ -15,6 +16,11 @@ Spectator.describe "Deferred stub expectation DSL" do dbl.foo end + it "returns the correct value" do + expect(dbl).to receive(:value).and_return(42) + expect(dbl.value).to eq(42) + end + it "matches when a message isn't received" do expect(dbl).to_not receive(:foo) end @@ -54,6 +60,11 @@ Spectator.describe "Deferred stub expectation DSL" do fake.foo(:bar) end + it "returns the correct value" do + expect(fake).to receive(:foo).and_return(42) + expect(fake.foo).to eq(42) + end + it "matches when a message isn't received" do expect(fake).to_not receive(:foo) end @@ -64,7 +75,7 @@ Spectator.describe "Deferred stub expectation DSL" do end it "matches when a message without arguments is received" do - expect(fake).to_not receive(:foo).with(:bar) + expect(fake).to_not receive(:foo).with(:bar).and_return(42) fake.foo end diff --git a/src/spectator/expectation.cr b/src/spectator/expectation.cr index dd7137e..bfc0248 100644 --- a/src/spectator/expectation.cr +++ b/src/spectator/expectation.cr @@ -147,6 +147,19 @@ module Spectator {% raise "The syntax `expect(...).to_eventually receive(...)` requires the expression passed to `expect` be stubbable (a mock or double)" unless T < ::Spectator::Stubbable || T < ::Spectator::StubbedType %} stubbable = @expression.value + unless stubbable._spectator_stub_for_method?(stub.method) + # Add stub without an argument constraint. + # Avoids confusing logic like this: + # ``` + # expect(dbl).to receive(:foo).with(:bar) + # dbl.foo(:baz) + # ``` + # Notice that `#foo` is called, but with different arguments. + # Normally this would raise an error, but that should be prevented. + unconstrained_stub = stub.with(Arguments.any) + stubbable._spectator_define_stub(unconstrained_stub) + end + stubbable._spectator_define_stub(stub) matcher = Matchers::ReceiveMatcher.new(stub) to_eventually(matcher, message) @@ -164,6 +177,19 @@ module Spectator {% raise "The syntax `expect(...).to_never receive(...)` requires the expression passed to `expect` be stubbable (a mock or double)" unless T < ::Spectator::Stubbable || T < ::Spectator::StubbedType %} stubbable = @expression.value + unless stubbable._spectator_stub_for_method?(stub.method) + # Add stub without an argument constraint. + # Avoids confusing logic like this: + # ``` + # expect(dbl).to receive(:foo).with(:bar) + # dbl.foo(:baz) + # ``` + # Notice that `#foo` is called, but with different arguments. + # Normally this would raise an error, but that should be prevented. + unconstrained_stub = stub.with(Arguments.any) + stubbable._spectator_define_stub(unconstrained_stub) + end + stubbable._spectator_define_stub(stub) matcher = Matchers::ReceiveMatcher.new(stub) to_never(matcher, message) diff --git a/src/spectator/mocks/double.cr b/src/spectator/mocks/double.cr index 66f9ca4..711f3bf 100644 --- a/src/spectator/mocks/double.cr +++ b/src/spectator/mocks/double.cr @@ -115,7 +115,7 @@ module Spectator stub end - private def _spectator_stub_for_method?(method : Symbol) : Bool + def _spectator_stub_for_method?(method : Symbol) : Bool @stubs.any? { |stub| stub.method == method } end diff --git a/src/spectator/mocks/mocked.cr b/src/spectator/mocks/mocked.cr index c2f9b38..be25ef0 100644 --- a/src/spectator/mocks/mocked.cr +++ b/src/spectator/mocks/mocked.cr @@ -34,7 +34,7 @@ module Spectator _spectator_stubs.find &.===(call) end - private def _spectator_stub_for_method?(method : Symbol) : Bool + def _spectator_stub_for_method?(method : Symbol) : Bool _spectator_stubs.any? { |stub| stub.method == method } end