From 225553127d937265b1d8081a0df80d4e596c33eb Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Wed, 1 Jun 2022 17:56:09 -0600 Subject: [PATCH] More mock tests --- spec/spectator/dsl/mocks/mock_spec.cr | 170 +++++++++++++++++++++----- 1 file changed, 139 insertions(+), 31 deletions(-) diff --git a/spec/spectator/dsl/mocks/mock_spec.cr b/spec/spectator/dsl/mocks/mock_spec.cr index 4309c61..e71a66d 100644 --- a/spec/spectator/dsl/mocks/mock_spec.cr +++ b/spec/spectator/dsl/mocks/mock_spec.cr @@ -21,51 +21,159 @@ Spectator.describe "Mock DSL", :smoke do end def method4 : Symbol + @_spectator_calls << :method4 yield end + + def method5 + @_spectator_calls << :method5 + yield.to_i + end + + def method6 + @_spectator_calls << :method6 + yield + end + + def method7(arg, *args, kwarg, **kwargs) + @_spectator_calls << :method7 + {arg, args, kwarg, kwargs} + end + + def method8(arg, *args, kwarg, **kwargs) + @_spectator_calls << :method8 + yield + {arg, args, kwarg, kwargs} + end end - context "specifying methods as keyword args" do - mock(ConcreteClass, method1: "stubbed", method2: :stubbed, method4: :block) - subject(fake) { mock(ConcreteClass) } - - it "defines a mock with methods" do - aggregate_failures do - expect(fake.method1).to eq("stubbed") - expect(fake.method2).to eq(:stubbed) - end + # method1 stubbed via mock block + # method2 stubbed via keyword args + # method3 not stubbed (calls original) + # method4 stubbed via mock block (yields) + # method5 stubbed via keyword args (yields) + # method6 not stubbed (calls original and yields) + # method7 not stubbed (calls original) testing args + # method8 not stubbed (calls original and yields) testing args + mock(ConcreteClass, method2: :stubbed, method5: 42) do + stub def method1 + "stubbed" end - it "defines a subclass" do - expect(fake).to be_a(ConcreteClass) + stub def method4 : Symbol + yield + :block end + end - it "compiles types without unions" do - aggregate_failures do - expect(fake.method1).to compile_as(String) - expect(fake.method2).to compile_as(Symbol) - end - end + subject(fake) { mock(ConcreteClass) } - def restricted(thing : ConcreteClass) - thing.method1 - end + it "defines a subclass" do + expect(fake).to be_a(ConcreteClass) + end - it "can be used in type restricted methods" do - expect(restricted(fake)).to eq("stubbed") - end + it "defines stubs in the block" do + expect(fake.method1).to eq("stubbed") + end - it "does not call the original method when stubbed" do - fake.method1 - fake.method2 - fake.method3("foo") - expect(fake._spectator_calls).to contain_exactly(:method3) - end + it "can stub methods defined in the block" do + stub = Spectator::ValueStub.new(:method1, "override") + expect { fake._spectator_define_stub(stub) }.to change { fake.method1 }.from("stubbed").to("override") + end - it "works with blocks" do - expect(fake.method4 { :wrong }).to eq(:block) + it "defines stubs from keyword arguments" do + expect(fake.method2).to eq(:stubbed) + end + + it "can stub methods from keyword arguments" do + stub = Spectator::ValueStub.new(:method2, :override) + expect { fake._spectator_define_stub(stub) }.to change { fake.method2 }.from(:stubbed).to(:override) + end + + it "calls the original implementation for methods not provided a stub" do + expect(fake.method3(:xyz)).to eq(:xyz) + end + + it "can stub methods after declaration" do + stub = Spectator::ValueStub.new(:method3, :abc) + expect { fake._spectator_define_stub(stub) }.to change { fake.method3(:xyz) }.from(:xyz).to(:abc) + end + + it "defines stubs with yield in the block" do + expect(fake.method4 { :wrong }).to eq(:block) + end + + it "can stub methods with yield in the block" do + stub = Spectator::ValueStub.new(:method4, :override) + expect { fake._spectator_define_stub(stub) }.to change { fake.method4 { :wrong } }.from(:block).to(:override) + end + + it "defines stubs with yield from keyword arguments" do + expect(fake.method5 { :wrong }).to eq(42) + end + + it "can stub methods with yield from keyword arguments" do + stub = Spectator::ValueStub.new(:method5, 123) + expect { fake._spectator_define_stub(stub) }.to change { fake.method5 { "0" } }.from(42).to(123) + end + + it "can stub yielding methods after declaration" do + stub = Spectator::ValueStub.new(:method6, :abc) + expect { fake._spectator_define_stub(stub) }.to change { fake.method6 { :xyz } }.from(:xyz).to(:abc) + end + + it "handles arguments correctly" do + args1 = fake.method7(1, 2, 3, kwarg: 4, x: 5, y: 6, z: 7) + args2 = fake.method8(1, 2, 3, kwarg: 4, x: 5, y: 6, z: 7) { :block } + aggregate_failures do + expect(args1).to eq({1, {2, 3}, 4, {x: 5, y: 6, z: 7}}) + expect(args2).to eq({1, {2, 3}, 4, {x: 5, y: 6, z: 7}}) end end + + xit "handles arguments correctly with stubs", pending: "Need ProcStub" do + stub1 = Spectator::ProcStub.new(:method7) { |args| args } + stub2 = Spectator::ProcStub.new(:method8) { |args| args } + fake._spectator_define_stub(stub1) + fake._spectator_define_stub(stub2) + args1 = fake.method7(1, 2, 3, kwarg: 4, x: 5, y: 6, z: 7) + args2 = fake.method8(1, 2, 3, kwarg: 4, x: 5, y: 6, z: 7) { :block } + aggregate_failures do + expect(args1).to eq({1, {2, 3}, 4, {x: 5, y: 6, z: 7}}) + expect(args2).to eq({1, {2, 3}, 4, {x: 5, y: 6, z: 7}}) + end + end + + it "compiles types without unions" do + aggregate_failures do + expect(fake.method1).to compile_as(String) + expect(fake.method2).to compile_as(Symbol) + expect(fake.method3(42)).to compile_as(Int32) + expect(fake.method4 { :foo }).to compile_as(Symbol) + expect(fake.method5 { "123" }).to compile_as(Int32) + expect(fake.method6 { "123" }).to compile_as(String) + end + end + + def restricted(thing : ConcreteClass) + thing.method1 + end + + it "can be used in type restricted methods" do + expect(restricted(fake)).to eq("stubbed") + end + + it "does not call the original method when stubbed" do + fake.method1 + fake.method2 + fake.method3("foo") + fake.method4 { :foo } + fake.method5 { "42" } + fake.method6 { 42 } + fake.method7(1, 2, 3, kwarg: 4, x: 5, y: 6, z: 7) + fake.method8(1, 2, 3, kwarg: 4, x: 5, y: 6, z: 7) { :block } + expect(fake._spectator_calls).to contain_exactly(:method3, :method6, :method7, :method8) + end end context "with an abstract class" do