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}})
|
||||
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)
|
||||
%value = ::Spectator::Value.new(({{method.id.symbolize}}), {{method.id.stringify}})
|
||||
::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