Rename Response to Stub

This commit is contained in:
Michael Miller 2022-03-05 20:09:56 -07:00
parent ced98778a4
commit ae14a47329
No known key found for this signature in database
GPG key ID: AC78B32D30CE34A2
5 changed files with 54 additions and 46 deletions

View file

@ -134,8 +134,8 @@ Spectator.describe Spectator::Double do
context "with arguments constraints" do context "with arguments constraints" do
let(arguments) { Spectator::Arguments.capture(/foo/) } let(arguments) { Spectator::Arguments.capture(/foo/) }
let(response) { Spectator::Response.new(:foo, "bar", arguments).as(Spectator::AbstractResponse) } let(stub) { Spectator::ValueStub.new(:foo, "bar", arguments).as(Spectator::Stub) }
subject(dbl) { Spectator::Double({foo: String}).new([response]) } subject(dbl) { Spectator::Double({foo: String}).new([stub]) }
it "returns the response when constraint satisfied" do it "returns the response when constraint satisfied" do
expect(dbl.foo("foobar")).to eq("bar") expect(dbl.foo("foobar")).to eq("bar")
@ -150,8 +150,8 @@ Spectator.describe Spectator::Double do
end end
context "with common object methods" do context "with common object methods" do
let(response) { Spectator::Response.new(:"same?", true, arguments).as(Spectator::AbstractResponse) } let(stub) { Spectator::ValueStub.new(:"same?", true, arguments).as(Spectator::Stub) }
subject(dbl) { Spectator::Double({"same?": Bool}).new([response]) } subject(dbl) { Spectator::Double({"same?": Bool}).new([stub]) }
it "returns the response when constraint satisfied" do it "returns the response when constraint satisfied" do
expect(dbl.same?("foobar")).to eq(true) expect(dbl.same?("foobar")).to eq(true)

View file

@ -1,11 +0,0 @@
module Spectator
# Untyped response to a method call (message).
abstract class AbstractResponse
# Name of the method this response is for.
getter method : Symbol
# Creates the base of the response.
def initialize(@method : Symbol)
end
end
end

View file

@ -1,5 +1,6 @@
require "./abstract_response"
require "./unexpected_message" require "./unexpected_message"
require "./stub"
require "./value_stub"
module Spectator module Spectator
# Stands in for an object for testing that a SUT calls expected methods. # Stands in for an object for testing that a SUT calls expected methods.
@ -9,25 +10,25 @@ module Spectator
# `NT` must be a type of `NamedTuple` that maps method names to their return types. # `NT` must be a type of `NamedTuple` that maps method names to their return types.
class Double(NT) class Double(NT)
# Stores responses to messages (method calls). # Stores responses to messages (method calls).
@responses : Array(AbstractResponse) @stubs : Array(Stub)
# Creates a double with pre-configures responses. # Creates a double with pre-configures responses.
# A *name* can be provided, otherwise it is considered an anonymous double. # A *name* can be provided, otherwise it is considered an anonymous double.
def initialize(@responses : Array(AbstractResponse), @name : String? = nil) def initialize(@stubs : Array(Stub), @name : String? = nil)
end end
def initialize(@name : String? = nil, **methods : **NT) def initialize(@name : String? = nil, **methods : **NT)
@responses = {% if NT.keys.empty? %} @stubs = {% if NT.keys.empty? %}
[] of AbstractResponse [] of Stub
{% else %} {% else %}
{% begin %} {% begin %}
[ [
{% for key in NT.keys %} {% for key in NT.keys %}
Response.new({{key.symbolize}}, methods[{{key.symbolize}}]).as(AbstractResponse), ValueStub.new({{key.symbolize}}, methods[{{key.symbolize}}]).as(Stub),
{% end %}
]
{% end %} {% end %}
{% end %} ]
{% end %}
{% end %}
end end
# Utility returning the double's name as a string. # Utility returning the double's name as a string.
@ -68,12 +69,12 @@ module Spectator
\{% if type <= {{meth.return_type}} %} \{% if type <= {{meth.return_type}} %}
# Return type appears to match configured type. # Return type appears to match configured type.
# Find a suitable response. # Find a suitable stub.
response = @responses.find &.===(call) stub = @stubs.find &.===(call)
if response if stub
# Return configured response. # Return configured response.
response.as(Response(\{{type}})).value stub.as(ValueStub(\{{type}})).value
else else
# Response not configured for this method/message. # Response not configured for this method/message.
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with #{arguments}") raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with #{arguments}")
@ -87,12 +88,12 @@ module Spectator
{% else %} {% else %}
# No return type restriction, return configured response. # No return type restriction, return configured response.
# Find a suitable response. # Find a suitable stub.
response = @responses.find &.===(call) stub = @stubs.find &.===(call)
if response if stub
# Return configured response. # Return configured response.
response.as(Response(\{{type}})).value stub.as(ValueStub(\{{type}})).value
else else
# Response not configured for this method/message. # Response not configured for this method/message.
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with #{arguments}") raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{meth.name}} (masking ancestor) with #{arguments}")
@ -119,12 +120,12 @@ module Spectator
call = MethodCall.new({{call.name.symbolize}}, arguments) call = MethodCall.new({{call.name.symbolize}}, arguments)
\{% if type = NT[{{call.name.symbolize}}] %} \{% if type = NT[{{call.name.symbolize}}] %}
# Find a suitable response. # Find a suitable stub.
response = @responses.find &.===(call) stub = @stubs.find &.===(call)
if response if stub
# Return configured response. # Return configured response.
response.as(Response(\{{type}})).value stub.as(ValueStub(\{{type}})).value
else else
# Response not configured for this method/message. # Response not configured for this method/message.
raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{call.name}} with #{arguments}") raise UnexpectedMessage.new("#{_spectator_double_name} received unexpected message :{{call.name}} with #{arguments}")

View file

@ -1,19 +1,22 @@
require "./abstract_arguments" require "./abstract_arguments"
require "./abstract_response" require "./arguments"
require "./method_call"
module Spectator module Spectator
class Response(T) < AbstractResponse # Untyped response to a method call (message).
# Return value. abstract class Stub
getter value : T # Name of the method this stub is for.
getter method : Symbol
# Arguments the method must have been called with to provide this response. # Arguments the method must have been called with to provide this response.
# Is nil when there's no constraint - only the method name must match. # Is nil when there's no constraint - only the method name must match.
getter constraint : AbstractArguments? getter constraint : AbstractArguments?
# Creates the response. # Creates the base of the stub.
def initialize(@method : Symbol, @value : T, @constraint : Arguments? = nil) def initialize(@method : Symbol, @constraint : Arguments? = nil)
end end
# Checks if a method call should receive the response from this stub.
def ===(call : MethodCall) def ===(call : MethodCall)
return false if method != call.method return false if method != call.method
return true unless constraint = @constraint return true unless constraint = @constraint

View file

@ -0,0 +1,15 @@
require "./arguments"
require "./stub"
module Spectator
# Stub that responds with a static value.
class ValueStub(T) < Stub
# Return value.
getter value : T
# Creates the stub.
def initialize(method : Symbol, @value : T, constraint : Arguments? = nil)
super(method, constraint)
end
end
end