From 8f80b10fc1d46d2f2e9d6676369b6e679f57a115 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sun, 18 Dec 2022 19:04:50 -0700 Subject: [PATCH] Support injecting mock functionality into modules Add mock registry for a single module. --- spec/docs/mocks_spec.cr | 19 ++++++++++++ src/spectator/mocks/mock.cr | 3 +- src/spectator/mocks/mock_registry.cr | 43 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/spectator/mocks/mock_registry.cr diff --git a/spec/docs/mocks_spec.cr b/spec/docs/mocks_spec.cr index 353c35e..58c16f8 100644 --- a/spec/docs/mocks_spec.cr +++ b/spec/docs/mocks_spec.cr @@ -205,6 +205,25 @@ Spectator.describe "Mocks Docs" do runnable = mock(Runnable2, command: "echo foo") expect(runnable.run_one).to eq("Running echo foo") end + + context "Injecting Mocks" do + module MyFileUtils + def self.rm_rf(path) + true + end + end + + inject_mock MyFileUtils do + stub def self.rm_rf(path) + "Simulating deletion of #{path}" + false + end + end + + specify do + expect(MyFileUtils.rm_rf("/")).to be_false + end + end end context "Injecting Mocks" do diff --git a/src/spectator/mocks/mock.cr b/src/spectator/mocks/mock.cr index 248d8c8..87e8e23 100644 --- a/src/spectator/mocks/mock.cr +++ b/src/spectator/mocks/mock.cr @@ -1,5 +1,6 @@ require "./method_call" require "./mocked" +require "./mock_registry" require "./reference_mock_registry" require "./stub" require "./stubbed_name" @@ -157,7 +158,7 @@ module Spectator {% elsif base == :struct %} @@_spectator_mock_registry = ::Spectator::ValueMockRegistry(self).new {% else %} - {% raise "Unsupported base type #{base} for injecting mock" %} + @@_spectator_mock_registry = ::Spectator::MockRegistry.new {% end %} private class_getter _spectator_stubs : ::Array(::Spectator::Stub) = [] of ::Spectator::Stub diff --git a/src/spectator/mocks/mock_registry.cr b/src/spectator/mocks/mock_registry.cr new file mode 100644 index 0000000..29390c6 --- /dev/null +++ b/src/spectator/mocks/mock_registry.cr @@ -0,0 +1,43 @@ +require "./mock_registry_entry" +require "./stub" + +module Spectator + # Stores collections of stubs for mocked types. + # + # This type is intended for all mocked modules that have functionality "injected." + # That is, the type itself has mock functionality bolted on. + # Adding instance members should be avoided, for instance, it could mess up serialization. + class MockRegistry + @entry : MockRegistryEntry? + + # Retrieves all stubs. + def [](_object = nil) + @entry.not_nil! + end + + # Retrieves all stubs. + def []?(_object = nil) + @entry + end + + # Retrieves all stubs. + # + # Yields to the block on the first retrieval. + # This allows a mock to populate the registry with initial stubs. + def fetch(object : Reference, & : -> Array(Stub)) + entry = @entry + if entry.nil? + entry = MockRegistryEntry.new + entry.stubs = yield + @entry = entry + else + entry + end + end + + # Clears all stubs defined for a mocked object. + def delete(object : Reference) : Nil + @entry = nil + end + end +end