mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Initial implementation of have_received
This commit is contained in:
parent
4f46c98a86
commit
694e2e6259
3 changed files with 159 additions and 0 deletions
70
spec/spectator/dsl/mocks/have_received_spec.cr
Normal file
70
spec/spectator/dsl/mocks/have_received_spec.cr
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
require "../../../spec_helper"
|
||||||
|
|
||||||
|
Spectator.describe "Stubbable receiver DSL" do
|
||||||
|
context "with a double" do
|
||||||
|
double(:dbl, foo: 42)
|
||||||
|
|
||||||
|
let(dbl) { double(:dbl) }
|
||||||
|
|
||||||
|
it "matches when a message is received" do
|
||||||
|
dbl.foo
|
||||||
|
expect(dbl).to have_received(:foo)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message isn't received" do
|
||||||
|
expect(dbl).to_not have_received(:foo)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message is received with matching arguments" do
|
||||||
|
dbl.foo(:bar)
|
||||||
|
expect(dbl).to have_received(:foo).with(:bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message without arguments is received" do
|
||||||
|
dbl.foo
|
||||||
|
expect(dbl).to_not have_received(:foo).with(:bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message without arguments isn't received" do
|
||||||
|
expect(dbl).to_not have_received(:foo).with(:bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message with arguments isn't received" do
|
||||||
|
dbl.foo(:bar)
|
||||||
|
expect(dbl).to_not have_received(:foo).with(:baz)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a mock" do
|
||||||
|
abstract class MyClass
|
||||||
|
abstract def foo(arg) : Int32
|
||||||
|
end
|
||||||
|
|
||||||
|
mock(MyClass, foo: 42)
|
||||||
|
|
||||||
|
let(fake) { mock(MyClass) }
|
||||||
|
|
||||||
|
it "matches when a message is received" do
|
||||||
|
fake.foo(:bar)
|
||||||
|
expect(fake).to have_received(:foo)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message isn't received" do
|
||||||
|
expect(fake).to_not have_received(:foo)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message is received with matching arguments" do
|
||||||
|
fake.foo(:bar)
|
||||||
|
expect(fake).to have_received(:foo).with(:bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message without arguments is received" do
|
||||||
|
expect(fake).to_not have_received(:foo).with(:bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "matches when a message with arguments isn't received" do
|
||||||
|
fake.foo(:bar)
|
||||||
|
expect(fake).to_not have_received(:foo).with(:baz)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -821,6 +821,12 @@ module Spectator::DSL
|
||||||
expect {{block}}.to raise_error({{type}}, {{message}})
|
expect {{block}}.to raise_error({{type}}, {{message}})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Indicates that a mock or double (stubbable type) should receive a message (have a method called).
|
||||||
|
# The *method* is the name of the method expected to be called.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# expect(dbl).to have_received(:foo)
|
||||||
|
# ```
|
||||||
macro have_received(method)
|
macro have_received(method)
|
||||||
%value = ::Spectator::Value.new(({{method.id.symbolize}}), {{method.id.stringify}})
|
%value = ::Spectator::Value.new(({{method.id.symbolize}}), {{method.id.stringify}})
|
||||||
::Spectator::Matchers::ReceiveMatcher.new(%value)
|
::Spectator::Matchers::ReceiveMatcher.new(%value)
|
||||||
|
|
83
src/spectator/matchers/receive_matcher.cr
Normal file
83
src/spectator/matchers/receive_matcher.cr
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
require "../mocks/stub"
|
||||||
|
require "../mocks/stubbable"
|
||||||
|
require "../mocks/stubbed_type"
|
||||||
|
require "./matcher"
|
||||||
|
|
||||||
|
module Spectator::Matchers
|
||||||
|
# Matcher that inspects stubbable objects for method calls.
|
||||||
|
struct ReceiveMatcher < Matcher
|
||||||
|
def initialize(@stub : Stub)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(expected : Expression(Symbol))
|
||||||
|
stub = NullStub.new(expected.value).as(Stub)
|
||||||
|
initialize(stub)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a new matcher with an argument constraint.
|
||||||
|
def with(*args, **kwargs) : self
|
||||||
|
stub = @stub.with(*args, **kwargs)
|
||||||
|
self.class.new(stub)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Short text about the matcher's purpose.
|
||||||
|
def description : String
|
||||||
|
"received #{@stub}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Actually performs the test against the expression (value or block).
|
||||||
|
def match(actual : Expression(Stubbable) | Expression(StubbedType)) : MatchData
|
||||||
|
stubbed = actual.value
|
||||||
|
if stubbed._spectator_calls.any? { |call| @stub === call }
|
||||||
|
SuccessfulMatchData.new("#{actual.label} received #{@stub}")
|
||||||
|
else
|
||||||
|
FailedMatchData.new("#{actual.label} received #{@stub}", "#{actual.label} did not receive #{@stub}", values(actual).to_a)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Actually performs the test against the expression (value or block).
|
||||||
|
def match(actual : Expression(T)) : MatchData forall T
|
||||||
|
{% raise "Value being checked with `have_received` must be stubbable (mock or double)." %}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Performs the test against the expression (value or block), but inverted.
|
||||||
|
def negated_match(actual : Expression(Stubbable) | Expression(StubbedType)) : MatchData
|
||||||
|
stubbed = actual.value
|
||||||
|
if stubbed._spectator_calls.any? { |call| @stub === call }
|
||||||
|
FailedMatchData.new("#{actual.label} did not receive #{@stub}", "#{actual.label} received #{@stub}", negated_values(actual).to_a)
|
||||||
|
else
|
||||||
|
SuccessfulMatchData.new("#{actual.label} did not receive #{@stub}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Performs the test against the expression (value or block), but inverted.
|
||||||
|
def negated_match(actual : Expression(T)) : MatchData forall T
|
||||||
|
{% raise "Value being checked with `have_received` must be stubbable (mock or double)." %}
|
||||||
|
end
|
||||||
|
|
||||||
|
private def match_data_description(actual : Expression(T)) : String forall T
|
||||||
|
match_data_description(actual.label)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def match_data_description(actual_label : String | Symbol) : String
|
||||||
|
"#{actual_label} #{description}"
|
||||||
|
end
|
||||||
|
|
||||||
|
private def match_data_description(actual_label : Nil) : String
|
||||||
|
description
|
||||||
|
end
|
||||||
|
|
||||||
|
# Additional information about the match failure.
|
||||||
|
private def values(actual : Expression(T)) forall T
|
||||||
|
{
|
||||||
|
expected: @stub.to_s,
|
||||||
|
actual: actual.value._spectator_calls.inspect,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Additional information about the match failure when negated.
|
||||||
|
private def negated_values(actual : Expression(T)) forall T
|
||||||
|
values(actual)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue