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

338 lines
8.1 KiB
Crystal
Raw Normal View History

2022-03-19 17:41:29 +00:00
require "../../../spec_helper"
2022-05-25 23:20:57 +00:00
Spectator.describe "Double DSL", :smoke do
2022-03-19 17:41:29 +00:00
context "specifying methods as keyword args" do
double(:test, foo: "foobar", bar: 42)
subject(dbl) { double(:test) }
it "defines a double with methods" do
aggregate_failures do
expect(dbl.foo).to eq("foobar")
expect(dbl.bar).to eq(42)
end
end
it "compiles types without unions" do
aggregate_failures do
expect(dbl.foo).to compile_as(String)
expect(dbl.bar).to compile_as(Int32)
end
end
context "with an unexpected message" do
it "raises an error" do
expect { dbl.baz }.to raise_error(Spectator::UnexpectedMessage, /baz/)
end
it "reports the double name" do
2022-03-20 00:57:39 +00:00
expect { dbl.baz }.to raise_error(Spectator::UnexpectedMessage, /:test/)
2022-03-19 17:41:29 +00:00
end
it "reports the arguments" do
expect { dbl.baz(:xyz, 123, a: "XYZ") }.to raise_error(Spectator::UnexpectedMessage, /\(:xyz, 123, a: "XYZ"\)/)
end
end
2022-03-19 23:04:51 +00:00
context "blocks" do
it "supports blocks" do
aggregate_failures do
expect(dbl.foo { nil }).to eq("foobar")
expect(dbl.bar { nil }).to eq(42)
end
end
it "supports blocks and has non-union return types" do
aggregate_failures do
expect(dbl.foo { nil }).to compile_as(String)
expect(dbl.bar { nil }).to compile_as(Int32)
end
end
it "fails on undefined messages" do
expect do
dbl.baz { nil }
end.to raise_error(Spectator::UnexpectedMessage, /baz/)
end
end
2022-03-19 17:41:29 +00:00
end
context "block with stubs" do
context "one method" do
double(:test2) do
stub def foo
"one method"
end
end
subject(dbl) { double(:test2) }
it "defines a double with methods" do
expect(dbl.foo).to eq("one method")
end
it "compiles types without unions" do
expect(dbl.foo).to compile_as(String)
end
end
context "two methods" do
double(:test3) do
stub def foo
"two methods"
end
stub def bar
42
end
end
subject(dbl) { double(:test3) }
it "defines a double with methods" do
aggregate_failures do
expect(dbl.foo).to eq("two methods")
expect(dbl.bar).to eq(42)
end
end
it "compiles types without unions" do
aggregate_failures do
expect(dbl.foo).to compile_as(String)
expect(dbl.bar).to compile_as(Int32)
end
end
end
context "empty block" do
double(:test4) do
end
subject(dbl) { double(:test4) }
it "defines a double" do
expect(dbl).to be_a(Spectator::Double)
end
end
context "stub-less method" do
double(:test5) do
def foo
"no stub"
end
end
subject(dbl) { double(:test5) }
it "defines a double with methods" do
expect(dbl.foo).to eq("no stub")
end
end
2022-03-19 18:52:23 +00:00
context "mixing keyword arguments" do
double(:test6, foo: "kwargs", bar: 42) do
stub def foo
"block"
end
stub def baz
"block"
end
stub def baz(value)
"block2"
end
end
subject(dbl) { double(:test6) }
it "overrides the keyword arguments with the block methods" do
expect(dbl.foo).to eq("block")
end
it "falls back to the keyword argument value for mismatched arguments" do
expect(dbl.foo(42)).to eq("kwargs")
end
it "can call methods defined only by keyword arguments" do
expect(dbl.bar).to eq(42)
end
it "can call methods defined only by the block" do
expect(dbl.baz).to eq("block")
end
it "can call methods defined by the block with different signatures" do
expect(dbl.baz(42)).to eq("block2")
end
end
2022-03-19 23:04:51 +00:00
context "methods accepting blocks" do
double(:test7) do
stub def foo
yield
end
stub def bar(& : Int32 -> String)
yield 42
end
end
subject(dbl) { double(:test7) }
it "defines the method and yields" do
expect(dbl.foo { :xyz }).to eq(:xyz)
end
it "matches methods with block argument type restrictions" do
expect(dbl.bar &.to_s).to eq("42")
end
end
end
2022-03-19 17:41:29 +00:00
describe "double naming" do
double(:Name, type: :symbol)
it "accepts a symbolic double name" do
dbl = double(:Name)
expect(dbl.type).to eq(:symbol)
end
it "accepts a string double name" do
dbl = double("Name")
expect(dbl.type).to eq(:symbol)
end
it "accepts a constant double name" do
dbl = double(Name)
expect(dbl.type).to eq(:symbol)
end
end
describe "predefined method stubs" do
double(:test8, foo: 42)
let(dbl) { double(:test8, foo: 7) }
it "overrides the original value" do
expect(dbl.foo).to eq(7)
end
end
2022-03-19 17:41:29 +00:00
describe "scope" do
double(:outer, scope: :outer)
double(:scope, scope: :outer)
it "finds a double in the same scope" do
dbl = double(:outer)
expect(dbl.scope).to eq(:outer)
end
it "uses an identically named double from the same scope" do
dbl = double(:scope)
expect(dbl.scope).to eq(:outer)
end
context "inner1" do
double(:inner, scope: :inner1)
double(:scope, scope: :inner1)
it "finds a double in the same scope" do
dbl = double(:inner)
expect(dbl.scope).to eq(:inner1)
end
it "uses an identically named double from the same scope" do
dbl = double(:scope)
expect(dbl.scope).to eq(:inner1)
end
context "nested" do
it "finds a double from a parent scope" do
aggregate_failures do
dbl = double(:inner)
expect(dbl.scope).to eq(:inner1)
dbl = double(:outer)
expect(dbl.scope).to eq(:outer)
end
end
it "uses the inner-most identically named double" do
dbl = double(:inner)
expect(dbl.scope).to eq(:inner1)
end
end
end
context "inner2" do
double(:inner, scope: :inner2)
double(:scope, scope: :inner2)
it "finds a double in the same scope" do
dbl = double(:inner)
expect(dbl.scope).to eq(:inner2)
end
it "uses an identically named double from the same scope" do
dbl = double(:scope)
expect(dbl.scope).to eq(:inner2)
end
context "nested" do
it "finds a double from a parent scope" do
aggregate_failures do
dbl = double(:inner)
expect(dbl.scope).to eq(:inner2)
dbl = double(:outer)
expect(dbl.scope).to eq(:outer)
end
end
it "uses the inner-most identically named double" do
dbl = double(:inner)
expect(dbl.scope).to eq(:inner2)
end
end
end
end
2022-04-02 21:58:08 +00:00
describe "context" do
double(:context_double, predefined: :predefined, override: :predefined) do
stub abstract def memoize : Symbol
stub def inline : Symbol
:inline # Memoized values can't be used here.
end
stub def reference : String
memoize.to_s
end
end
let(memoize) { :memoize }
let(override) { :override }
let(dbl) { double(:context_double, override: override) }
before_each { allow(dbl).to receive(:memoize).and_return(memoize) }
it "doesn't change predefined values" do
expect(dbl.predefined).to eq(:predefined)
end
it "can use memoized values for overrides" do
expect(dbl.override).to eq(:override)
end
it "can use memoized values for stubs" do
expect(dbl.memoize).to eq(:memoize)
end
it "can override inline stubs" do
expect { allow(dbl).to receive(:inline).and_return(override) }.to change { dbl.inline }.from(:inline).to(:override)
end
it "can reference memoized values with indirection" do
expect { allow(dbl).to receive(:memoize).and_return(override) }.to change { dbl.reference }.from("memoize").to("override")
end
end
2022-03-19 17:41:29 +00:00
end