mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Record calls on injected mocks
This commit is contained in:
parent
8aed5027aa
commit
c8ec0ad02a
6 changed files with 146 additions and 55 deletions
|
@ -6,56 +6,88 @@ Spectator.describe Spectator::ReferenceMockRegistry do
|
|||
let(stub) { Spectator::ValueStub.new(:test, 42) }
|
||||
let(stubs) { [stub] of Spectator::Stub }
|
||||
let(no_stubs) { [] of Spectator::Stub }
|
||||
let(call) { Spectator::MethodCall.capture(:method2, 5) }
|
||||
let(calls) { [call] }
|
||||
let(no_calls) { [] of Spectator::MethodCall }
|
||||
|
||||
it "initially has no stubs" do
|
||||
expect(registry[obj]).to be_empty
|
||||
expect(registry[obj].stubs).to be_empty
|
||||
end
|
||||
|
||||
it "initially has no calls" do
|
||||
expect(registry[obj].calls).to be_empty
|
||||
end
|
||||
|
||||
it "stores stubs for an object" do
|
||||
expect { registry[obj] << stub }.to change { registry[obj] }.from(no_stubs).to(stubs)
|
||||
expect { registry[obj].stubs << stub }.to change { registry[obj].stubs }.from(no_stubs).to(stubs)
|
||||
end
|
||||
|
||||
it "stores calls for an object" do
|
||||
expect { registry[obj].calls << call }.to change { registry[obj].calls }.from(no_calls).to(calls)
|
||||
end
|
||||
|
||||
it "isolates stubs between different objects" do
|
||||
obj1 = "foo"
|
||||
obj2 = "bar"
|
||||
registry[obj2] << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry[obj1] << stub }.to_not change { registry[obj2] }
|
||||
registry[obj2].stubs << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry[obj1].stubs << stub }.to_not change { registry[obj2].stubs }
|
||||
end
|
||||
|
||||
it "isolates calls between different objects" do
|
||||
obj1 = "foo"
|
||||
obj2 = "bar"
|
||||
registry[obj2].calls << Spectator::MethodCall.capture(:method1, 42)
|
||||
expect { registry[obj1].calls << call }.to_not change { registry[obj2].calls }
|
||||
end
|
||||
|
||||
describe "#fetch" do
|
||||
it "retrieves existing stubs" do
|
||||
registry[obj] << stub
|
||||
expect(registry.fetch(obj) { no_stubs }).to eq(stubs)
|
||||
registry[obj].stubs << stub
|
||||
expect(registry.fetch(obj) { no_stubs }.stubs).to eq(stubs)
|
||||
end
|
||||
|
||||
it "stores stubs on the first retrieval" do
|
||||
expect(registry.fetch(obj) { stubs }).to eq(stubs)
|
||||
expect(registry.fetch(obj) { stubs }.stubs).to eq(stubs)
|
||||
end
|
||||
|
||||
it "isolates stubs between different objects" do
|
||||
obj1 = "foo"
|
||||
obj2 = "bar"
|
||||
registry[obj2] << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.fetch(obj1) { no_stubs } }.to_not change { registry[obj2] }
|
||||
registry[obj2].stubs << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.fetch(obj1) { no_stubs }.stubs }.to_not change { registry[obj2].stubs }
|
||||
end
|
||||
|
||||
it "isolates calls between different objects" do
|
||||
obj1 = "foo"
|
||||
obj2 = "bar"
|
||||
registry[obj2].calls << Spectator::MethodCall.capture(:method1, 42)
|
||||
expect { registry.fetch(obj1) { no_stubs }.calls }.to_not change { registry[obj2].calls }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#delete" do
|
||||
it "clears stubs for an object" do
|
||||
registry[obj] << stub
|
||||
expect { registry.delete(obj) }.to change { registry[obj] }.from(stubs).to(no_stubs)
|
||||
registry[obj].stubs << stub
|
||||
expect { registry.delete(obj) }.to change { registry[obj].stubs }.from(stubs).to(no_stubs)
|
||||
end
|
||||
|
||||
it "doesn't clear initial stubs provided with #fetch" do
|
||||
registry[obj] << Spectator::ValueStub.new(:stub2, 42)
|
||||
expect { registry.delete(obj) }.to change { registry.fetch(obj) { stubs } }.to(stubs)
|
||||
registry[obj].stubs << Spectator::ValueStub.new(:stub2, 42)
|
||||
expect { registry.delete(obj) }.to change { registry.fetch(obj) { stubs }.stubs }.to(stubs)
|
||||
end
|
||||
|
||||
it "isolates stubs between different objects" do
|
||||
obj1 = "foo"
|
||||
obj2 = "bar"
|
||||
registry[obj2] << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.delete(obj1) }.to_not change { registry[obj2] }
|
||||
registry[obj2].stubs << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.delete(obj1) }.to_not change { registry[obj2].stubs }
|
||||
end
|
||||
|
||||
it "isolates calls between different objects" do
|
||||
obj1 = "foo"
|
||||
obj2 = "bar"
|
||||
registry[obj2].calls << Spectator::MethodCall.capture(:method1, 42)
|
||||
expect { registry.delete(obj1) }.to_not change { registry[obj2].calls }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,56 +6,88 @@ Spectator.describe Spectator::ValueMockRegistry do
|
|||
let(stub) { Spectator::ValueStub.new(:test, 5) }
|
||||
let(stubs) { [stub] of Spectator::Stub }
|
||||
let(no_stubs) { [] of Spectator::Stub }
|
||||
let(call) { Spectator::MethodCall.capture(:method2, 5) }
|
||||
let(calls) { [call] }
|
||||
let(no_calls) { [] of Spectator::MethodCall }
|
||||
|
||||
it "initially has no stubs" do
|
||||
expect(registry[obj]).to be_empty
|
||||
expect(registry[obj].stubs).to be_empty
|
||||
end
|
||||
|
||||
it "initially has no calls" do
|
||||
expect(registry[obj].calls).to be_empty
|
||||
end
|
||||
|
||||
it "stores stubs for an object" do
|
||||
expect { registry[obj] << stub }.to change { registry[obj] }.from(no_stubs).to(stubs)
|
||||
expect { registry[obj].stubs << stub }.to change { registry[obj].stubs }.from(no_stubs).to(stubs)
|
||||
end
|
||||
|
||||
it "stores calls for an object" do
|
||||
expect { registry[obj].calls << call }.to change { registry[obj].calls }.from(no_calls).to(calls)
|
||||
end
|
||||
|
||||
it "isolates stubs between different objects" do
|
||||
obj1 = 1
|
||||
obj2 = 2
|
||||
registry[obj2] << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry[obj1] << stub }.to_not change { registry[obj2] }
|
||||
registry[obj2].stubs << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry[obj1].stubs << stub }.to_not change { registry[obj2].stubs }
|
||||
end
|
||||
|
||||
it "isolates calls between different objects" do
|
||||
obj1 = 1
|
||||
obj2 = 2
|
||||
registry[obj2].calls << Spectator::MethodCall.capture(:method1, 42)
|
||||
expect { registry[obj1].calls << call }.to_not change { registry[obj2].calls }
|
||||
end
|
||||
|
||||
describe "#fetch" do
|
||||
it "retrieves existing stubs" do
|
||||
registry[obj] << stub
|
||||
expect(registry.fetch(obj) { no_stubs }).to eq(stubs)
|
||||
registry[obj].stubs << stub
|
||||
expect(registry.fetch(obj) { no_stubs }.stubs).to eq(stubs)
|
||||
end
|
||||
|
||||
it "stores stubs on the first retrieval" do
|
||||
expect(registry.fetch(obj) { stubs }).to eq(stubs)
|
||||
expect(registry.fetch(obj) { stubs }.stubs).to eq(stubs)
|
||||
end
|
||||
|
||||
it "isolates stubs between different objects" do
|
||||
obj1 = 1
|
||||
obj2 = 2
|
||||
registry[obj2] << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.fetch(obj1) { no_stubs } }.to_not change { registry[obj2] }
|
||||
registry[obj2].stubs << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.fetch(obj1) { no_stubs }.stubs }.to_not change { registry[obj2].stubs }
|
||||
end
|
||||
|
||||
it "isolates calls between different objects" do
|
||||
obj1 = 1
|
||||
obj2 = 2
|
||||
registry[obj2].calls << Spectator::MethodCall.capture(:method1, 42)
|
||||
expect { registry.fetch(obj1) { no_stubs }.calls }.to_not change { registry[obj2].calls }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#delete" do
|
||||
it "clears stubs for an object" do
|
||||
registry[obj] << stub
|
||||
expect { registry.delete(obj) }.to change { registry[obj] }.from(stubs).to(no_stubs)
|
||||
registry[obj].stubs << stub
|
||||
expect { registry.delete(obj) }.to change { registry[obj].stubs }.from(stubs).to(no_stubs)
|
||||
end
|
||||
|
||||
it "doesn't clear initial stubs provided with #fetch" do
|
||||
registry[obj] << Spectator::ValueStub.new(:stub2, 42)
|
||||
expect { registry.delete(obj) }.to change { registry.fetch(obj) { stubs } }.to(stubs)
|
||||
registry[obj].stubs << Spectator::ValueStub.new(:stub2, 42)
|
||||
expect { registry.delete(obj) }.to change { registry.fetch(obj) { stubs }.stubs }.to(stubs)
|
||||
end
|
||||
|
||||
it "isolates stubs between different objects" do
|
||||
obj1 = 1
|
||||
obj2 = 2
|
||||
registry[obj2] << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.delete(obj1) }.to_not change { registry[obj2] }
|
||||
registry[obj2].stubs << Spectator::ValueStub.new(:obj2, 42)
|
||||
expect { registry.delete(obj1) }.to_not change { registry[obj2].stubs }
|
||||
end
|
||||
|
||||
it "isolates calls between different objects" do
|
||||
obj1 = 1
|
||||
obj2 = 2
|
||||
registry[obj2].calls << Spectator::MethodCall.capture(:method1, 42)
|
||||
expect { registry.delete(obj1) }.to_not change { registry[obj2].calls }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -113,15 +113,10 @@ module Spectator
|
|||
{% end %}
|
||||
|
||||
private def _spectator_stubs
|
||||
@@_spectator_mock_registry.fetch(self) do
|
||||
{% begin %}
|
||||
[
|
||||
{% for key, value in value_methods %}
|
||||
::Spectator::ValueStub.new({{key.id.symbolize}}, {{value}}),
|
||||
{% end %}
|
||||
] of ::Spectator::Stub
|
||||
{% end %}
|
||||
entry = @@_spectator_mock_registry.fetch(self) do
|
||||
_spectator_default_stubs
|
||||
end
|
||||
entry.stubs
|
||||
end
|
||||
|
||||
def _spectator_clear_stubs : Nil
|
||||
|
@ -129,7 +124,20 @@ module Spectator
|
|||
end
|
||||
|
||||
def _spectator_calls
|
||||
[] of ::Spectator::MethodCall
|
||||
entry = @@_spectator_mock_registry.fetch(self) do
|
||||
_spectator_default_stubs
|
||||
end
|
||||
entry.calls
|
||||
end
|
||||
|
||||
private def _spectator_default_stubs
|
||||
{% begin %}
|
||||
[
|
||||
{% for key, value in value_methods %}
|
||||
::Spectator::ValueStub.new({{key.id.symbolize}}, {{value}}),
|
||||
{% end %}
|
||||
] of ::Spectator::Stub
|
||||
{% end %}
|
||||
end
|
||||
|
||||
# Returns the mock's name formatted for user output.
|
||||
|
|
13
src/spectator/mocks/mock_registry_entry.cr
Normal file
13
src/spectator/mocks/mock_registry_entry.cr
Normal file
|
@ -0,0 +1,13 @@
|
|||
require "./method_call"
|
||||
require "./stub"
|
||||
|
||||
module Spectator
|
||||
# Stubs and calls for a mock.
|
||||
private struct MockRegistryEntry
|
||||
# Retrieves all stubs defined for a mock.
|
||||
property stubs = [] of Stub
|
||||
|
||||
# Retrieves all calls to stubbed methods.
|
||||
getter calls = [] of MethodCall
|
||||
end
|
||||
end
|
|
@ -1,3 +1,4 @@
|
|||
require "./mock_registry_entry"
|
||||
require "./stub"
|
||||
|
||||
module Spectator
|
||||
|
@ -9,19 +10,19 @@ module Spectator
|
|||
# This registry works around that by mapping mocks (via their memory address) to a collection of stubs.
|
||||
# Doing so prevents adding data to the mocked type.
|
||||
class ReferenceMockRegistry
|
||||
@object_stubs : Hash(Void*, Array(Stub))
|
||||
@entries : Hash(Void*, MockRegistryEntry)
|
||||
|
||||
# Creates an empty registry.
|
||||
def initialize
|
||||
@object_stubs = Hash(Void*, Array(Stub)).new do |hash, key|
|
||||
hash[key] = [] of Stub
|
||||
@entries = Hash(Void*, MockRegistryEntry).new do |hash, key|
|
||||
hash[key] = MockRegistryEntry.new
|
||||
end
|
||||
end
|
||||
|
||||
# Retrieves all stubs defined for a mocked object.
|
||||
def [](object : Reference) : Array(Stub)
|
||||
def [](object : Reference)
|
||||
key = Box.box(object)
|
||||
@object_stubs[key]
|
||||
@entries[key]
|
||||
end
|
||||
|
||||
# Retrieves all stubs defined for a mocked object.
|
||||
|
@ -30,15 +31,17 @@ module Spectator
|
|||
# This allows a mock to populate the registry with initial stubs.
|
||||
def fetch(object : Reference, & : -> Array(Stub))
|
||||
key = Box.box(object)
|
||||
@object_stubs.fetch(key) do
|
||||
@object_stubs[key] = yield
|
||||
@entries.fetch(key) do
|
||||
entry = MockRegistryEntry.new
|
||||
entry.stubs = yield
|
||||
@entries[key] = entry
|
||||
end
|
||||
end
|
||||
|
||||
# Clears all stubs defined for a mocked object.
|
||||
def delete(object : Reference) : Nil
|
||||
key = Box.box(object)
|
||||
@object_stubs.delete(key)
|
||||
@entries.delete(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require "string_pool"
|
||||
require "./mock_registry_entry"
|
||||
require "./stub"
|
||||
|
||||
module Spectator
|
||||
|
@ -13,19 +14,19 @@ module Spectator
|
|||
# Doing so prevents adding data to the mocked type.
|
||||
class ValueMockRegistry(T)
|
||||
@pool = StringPool.new # Used to de-dup values.
|
||||
@object_stubs : Hash(String, Array(Stub))
|
||||
@entries : Hash(String, MockRegistryEntry)
|
||||
|
||||
# Creates an empty registry.
|
||||
def initialize
|
||||
@object_stubs = Hash(String, Array(Stub)).new do |hash, key|
|
||||
hash[key] = [] of Stub
|
||||
@entries = Hash(String, MockRegistryEntry).new do |hash, key|
|
||||
hash[key] = MockRegistryEntry.new
|
||||
end
|
||||
end
|
||||
|
||||
# Retrieves all stubs defined for a mocked object.
|
||||
def [](object : T) : Array(Stub)
|
||||
def [](object : T)
|
||||
key = value_bytes(object)
|
||||
@object_stubs[key]
|
||||
@entries[key]
|
||||
end
|
||||
|
||||
# Retrieves all stubs defined for a mocked object.
|
||||
|
@ -34,15 +35,17 @@ module Spectator
|
|||
# This allows a mock to populate the registry with initial stubs.
|
||||
def fetch(object : T, & : -> Array(Stub))
|
||||
key = value_bytes(object)
|
||||
@object_stubs.fetch(key) do
|
||||
@object_stubs[key] = yield
|
||||
@entries.fetch(key) do
|
||||
entry = MockRegistryEntry.new
|
||||
entry.stubs = yield
|
||||
@entries[key] = entry
|
||||
end
|
||||
end
|
||||
|
||||
# Clears all stubs defined for a mocked object.
|
||||
def delete(object : T) : Nil
|
||||
key = value_bytes(object)
|
||||
@object_stubs.delete(key)
|
||||
@entries.delete(key)
|
||||
end
|
||||
|
||||
# Extracts heap-managed bytes for a value.
|
||||
|
|
Loading…
Reference in a new issue