shard-spectator/spec/spectator/mocks/double_spec.cr

250 lines
9.4 KiB
Crystal
Raw Normal View History

2022-03-04 05:10:09 +00:00
require "../../spec_helper"
Spectator.describe Spectator::Double do
2022-03-12 20:54:40 +00:00
Spectator::Double.define(EmptyDouble)
Spectator::Double.define(FooBarDouble, "dbl-name", foo: 42, bar: "baz")
2022-03-12 16:39:32 +00:00
2022-03-12 20:54:40 +00:00
# The subject `dbl` must be carefully used in sub-contexts, otherwise it pollutes parent scopes.
# This changes the type of `dbl` to `Double`, which produces a union of methods and their return types.
context "plain double" do
subject(dbl) { FooBarDouble.new }
2022-03-04 05:10:09 +00:00
2022-03-12 20:54:40 +00:00
it "responds to defined messages" do
aggregate_failures do
expect(dbl.foo).to eq(42)
expect(dbl.bar).to eq("baz")
end
2022-03-04 05:10:09 +00:00
end
2022-03-12 20:54:40 +00:00
it "fails on undefined messages" do
expect { dbl.baz }.to raise_error(Spectator::UnexpectedMessage, /baz/)
end
2022-03-04 05:10:09 +00:00
2022-03-12 20:54:40 +00:00
it "reports the name in errors" do
expect { dbl.baz }.to raise_error(/"dbl-name"/)
end
2022-03-12 20:54:40 +00:00
it "reports arguments" do
expect { dbl.baz(123, "qux", field: :value) }.to raise_error(Spectator::UnexpectedMessage, /\(123, "qux", field: :value\)/)
end
it "has a non-union return type" do
aggregate_failures do
expect(dbl.foo).to compile_as(Int32)
expect(dbl.bar).to compile_as(String)
end
end
2022-03-04 05:31:40 +00:00
2022-03-12 20:54:40 +00:00
it "uses nil for undefined messages" do
expect { dbl.baz }.to compile_as(Nil)
2022-03-12 16:39:32 +00:00
end
end
2022-03-04 05:31:40 +00:00
context "without a double name" do
2022-03-12 20:54:40 +00:00
Spectator::Double.define(NamelessDouble, foo: 42)
2022-03-12 16:39:32 +00:00
2022-03-12 20:54:40 +00:00
subject(dbl) { NamelessDouble.new }
2022-03-04 05:31:40 +00:00
it "reports as anonymous" do
expect { dbl.baz }.to raise_error(/anonymous/i)
end
end
2022-03-04 05:10:09 +00:00
context "with common object methods" do
subject(dbl) do
2022-03-12 20:54:40 +00:00
EmptyDouble.new([
Spectator::ValueStub.new(:"!=", "!="),
Spectator::ValueStub.new(:"!~", "!~"),
Spectator::ValueStub.new(:"==", "=="),
Spectator::ValueStub.new(:"===", "==="),
Spectator::ValueStub.new(:"=~", "=~"),
Spectator::ValueStub.new(:class, "class"),
Spectator::ValueStub.new(:dup, "dup"),
Spectator::ValueStub.new(:hash, "hash"),
Spectator::ValueStub.new(:"in?", true),
Spectator::ValueStub.new(:inspect, "inspect"),
Spectator::ValueStub.new(:itself, "itself"),
Spectator::ValueStub.new(:"not_nil!", "not_nil!"),
Spectator::ValueStub.new(:pretty_inspect, "pretty_inspect"),
Spectator::ValueStub.new(:tap, "tap"),
Spectator::ValueStub.new(:to_json, "to_json"),
Spectator::ValueStub.new(:to_pretty_json, "to_pretty_json"),
Spectator::ValueStub.new(:to_s, "to_s"),
Spectator::ValueStub.new(:to_yaml, "to_yaml"),
Spectator::ValueStub.new(:try, "try"),
Spectator::ValueStub.new(:object_id, 42_u64),
Spectator::ValueStub.new(:"same?", true),
2022-03-12 20:54:40 +00:00
] of Spectator::Stub)
2022-03-04 05:10:09 +00:00
end
it "responds with defined messages" do
aggregate_failures do
expect(dbl.!=(42)).to eq("!=")
expect(dbl.!~(42)).to eq("!~")
expect(dbl.==(42)).to eq("==")
expect(dbl.===(42)).to eq("===")
expect(dbl.=~(42)).to eq("=~")
expect(dbl.class).to eq("class")
expect(dbl.dup).to eq("dup")
expect(dbl.hash(42)).to eq("hash")
expect(dbl.hash).to eq("hash")
expect(dbl.in?(42)).to eq(true)
expect(dbl.in?(1, 2, 3)).to eq(true)
expect(dbl.inspect).to eq("inspect")
expect(dbl.itself).to eq("itself")
expect(dbl.not_nil!).to eq("not_nil!")
expect(dbl.pretty_inspect).to eq("pretty_inspect")
expect(dbl.tap { nil }).to eq("tap")
expect(dbl.to_json).to eq("to_json")
expect(dbl.to_pretty_json).to eq("to_pretty_json")
expect(dbl.to_s).to eq("to_s")
expect(dbl.to_yaml).to eq("to_yaml")
expect(dbl.try { nil }).to eq("try")
expect(dbl.object_id).to eq(42_u64)
expect(dbl.same?(dbl)).to eq(true)
expect(dbl.same?(nil)).to eq(true)
2022-03-04 05:10:09 +00:00
end
end
2022-03-12 16:39:32 +00:00
it "has a non-union return type" do
expect(dbl.inspect).to compile_as(String)
end
2022-03-04 05:10:09 +00:00
end
context "without common object methods" do
2022-03-12 20:54:40 +00:00
subject(dbl) { EmptyDouble.new }
2022-03-04 05:10:09 +00:00
it "raises with undefined messages" do
io = IO::Memory.new
pp = PrettyPrint.new(io)
aggregate_failures do
2022-03-12 20:54:40 +00:00
expect { dbl.!=(42) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.!~(42) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.==(42) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.===(42) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.=~(42) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.class }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.dup }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.hash(42) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.hash }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.in?(42) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.in?(1, 2, 3) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.inspect }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.itself }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.not_nil! }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.pretty_inspect }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.pretty_inspect(io) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.pretty_print(pp) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.tap { nil } }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_json }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_json(io) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_pretty_json }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_pretty_json(io) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_s }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_s(io) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_yaml }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.to_yaml(io) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.try { nil } }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.object_id }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.same?(dbl) }.to raise_error(Spectator::UnexpectedMessage)
expect { dbl.same?(nil) }.to raise_error(Spectator::UnexpectedMessage)
2022-03-04 05:10:09 +00:00
end
end
it "reports arguments" do
expect { dbl.same?(123) }.to raise_error(Spectator::UnexpectedMessage, /\(123\)/)
end
2022-03-04 05:10:09 +00:00
end
2022-03-05 22:00:11 +00:00
context "with arguments constraints" do
let(arguments) { Spectator::Arguments.capture(/foo/) }
2022-03-12 20:54:40 +00:00
context "without common object methods" do
Spectator::Double.define(TestDouble) do
abstract_stub abstract def foo(value) : String
end
2022-03-05 22:00:11 +00:00
2022-03-12 20:54:40 +00:00
let(stub) { Spectator::ValueStub.new(:foo, "bar", arguments).as(Spectator::Stub) }
subject(dbl) { TestDouble.new([stub]) }
2022-03-05 22:00:11 +00:00
2022-03-12 20:54:40 +00:00
it "returns the response when constraint satisfied" do
expect(dbl.foo("foobar")).to eq("bar")
end
2022-03-05 22:39:35 +00:00
2022-03-12 20:54:40 +00:00
it "raises an error when constraint unsatisfied" do
expect { dbl.foo("baz") }.to raise_error(Spectator::UnexpectedMessage)
end
it "raises an error when argument count doesn't match" do
expect { dbl.foo }.to raise_error(Spectator::UnexpectedMessage)
end
it "has a non-union return type" do
expect(dbl.foo("foobar")).to compile_as(String)
end
2022-03-12 16:39:32 +00:00
end
2022-03-05 22:39:35 +00:00
context "with common object methods" do
2022-03-12 20:54:40 +00:00
Spectator::Double.define(TestDouble) do
stub abstract def same?(other : Reference) : Bool
end
2022-03-06 03:09:56 +00:00
let(stub) { Spectator::ValueStub.new(:"same?", true, arguments).as(Spectator::Stub) }
2022-03-12 20:54:40 +00:00
subject(dbl) { TestDouble.new([stub]) }
2022-03-05 22:39:35 +00:00
it "returns the response when constraint satisfied" do
expect(dbl.same?("foobar")).to eq(true)
end
it "raises an error when constraint unsatisfied" do
2022-03-12 20:54:40 +00:00
expect { dbl.same?("baz") }.to raise_error(Spectator::UnexpectedMessage)
2022-03-05 22:39:35 +00:00
end
it "raises an error when argument count doesn't match" do
expect { dbl.same? }.to raise_error(Spectator::UnexpectedMessage)
end
2022-03-12 16:39:32 +00:00
it "has a non-union return type" do
expect(dbl.same?("foobar")).to compile_as(Bool)
end
2022-03-05 22:39:35 +00:00
end
2022-03-05 22:00:11 +00:00
end
2022-03-12 21:31:19 +00:00
describe "#_spectator_define_stub" do
subject(dbl) { FooBarDouble.new }
let(stub3) { Spectator::ValueStub.new(:foo, 3) }
let(stub5) { Spectator::ValueStub.new(:foo, 5) }
let(stub7) { Spectator::ValueStub.new(:foo, 7, Spectator::Arguments.capture(:lucky)) }
it "overrides an existing method" do
expect { dbl._spectator_define_stub(stub3) }.to change { dbl.foo }.from(42).to(3)
end
it "replaces an existing stub" do
dbl._spectator_define_stub(stub3)
expect { dbl._spectator_define_stub(stub5) }.to change { dbl.foo }.from(3).to(5)
end
it "doesn't affect other methods" do
expect { dbl._spectator_define_stub(stub5) }.to_not change { dbl.bar }
end
it "picks the correct stub based on arguments" do
dbl._spectator_define_stub(stub5)
dbl._spectator_define_stub(stub7)
aggregate_failures do
expect(dbl.foo).to eq(5)
expect(dbl.foo(:lucky)).to eq(7)
end
end
it "only uses a stub if an argument constraint is met" do
dbl._spectator_define_stub(stub7)
aggregate_failures do
expect(dbl.foo).to eq(42)
expect(dbl.foo(:lucky)).to eq(7)
end
end
end
2022-03-04 05:10:09 +00:00
end