This page contains documentation specific to using the Spec library with this shard.
To enable mocks, require the shard from inside spec_helper.cr
.
It should be required after Spec so that the Mocks shard can detect it.
require "spec"
require "mocks"
Inside of your spec files, the "can" and "should" syntaxes are used.
Mocks are defined by using the mock
keyword outside of test code.
The mock
keyword cannot be used inside a describe
, context
, it
or similar blocks, such as hooks.
The syntax for mock
is:
mock NewType < ExistingType
Where NewType
is the name of the type to create and ExistingType
is the type to mock (stub and replace) the behavior of.
For instance:
class ExistingType
# ...
end
mock NewType < ExistingType
Default stubs can be defined quickly by adding them as keyword arguments.
mock NewType < ExistingType, method1: 1, method2: 2
More complex methods can be defined inside a block.
mock NewType < ExistingType do
def complex_method
# ...
end
end
One style of defining mocks is to place them at the top of the spec file they're used in.
The private
visibility modifier should be used to avoid name collisions when multiple spec files are ran together.
private mock TestMock < ExistingType
describe "MyService" do
it "works" do
# ...
end
end
An alternative is to create a mocks
(or similarly named) directory containing all of the mock definitions.
This can then be included from spec_helper.cr
, like so:
require "mocks"
require "./mocks/**"
Mocks can be instantiated in example blocks and hooks.
Simply call new
on the mock's type.
private mock TestMock < ExistingType
describe "MyService" do
it "works" do
my_mock = TestMock.new
# ...
end
end
Mocks cannot be used outside of a test scope (e.g. an it
block).
Specifically, they cannot be used in before_all
, after_all
, and around_all
hooks.
Defining a double is similar to defining a mock.
Doubles are defined by using the double
keyword outside of test code.
The double
keyword cannot be used inside a describe
, context
, it
or similar blocks, such as hooks.
The syntax for double
is:
double NewDouble
Where NewDouble
is the name of the type to create.
NOTE: It is highly recommended that doubles in spec files are defined with the private
visibility modifier.
This avoids collisions with the same name in multiple files.
Default stubs can be defined quickly by adding them as keyword arguments.
private double NewDouble, method1: 1, method2: 2
More complex methods can be defined inside a block.
private double NewDouble do
def complex_method
# ...
end
end
Doubles can be instantiated in example blocks and hooks.
Simply call new
on the doubles's type.
private double TestDouble
describe "MyService" do
it "works" do
my_double = TestDouble.new
# ...
end
end
A lazy double can be created with the new_double
method.
This creates a double without the need to define it ahead of time.
describe "MyService" do
it "works" do
my_double = new_double(value: 42)
my_double.value.should eq(42)
end
end
Doubles cannot be used outside of a test scope (e.g. an it
block).
Specifically, they cannot be used in before_all
, after_all
, and around_all
hooks.
Stubs are defined and applied to stubbable objects with the can
method.
The can
method expects a stub as an argument.
Typically this is written similarly to the object.should
syntax with a space after can
and no parenthesis.
The fluent language syntax is used to construct a stub that is passed to can
.
Use receive
to start the fluent syntax.
private double TestDouble, value: 0
it "works" do
my_double = TestDouble.new
my_double.can receive(:value).and_return(42)
end
The can
method is only available on stubbable objects (mocks and doubles).
A compiler error will occur when attempting to use can
on an object that isn't stubbable.
it "can't stub non-stubbable objects" do
string = "foobar"
string.can receive(:size).and_return(42) # Error!
string.size.should eq(42)
end
Results in:
Error: undefined method 'can' for String
Expectations are created using Spec's should
and should_not
methods.
Use the have_received
method to start the fluent language syntax to describe the call.
private double TestDouble, value: 0
it "works" do
my_double = TestDouble.new
my_double.value
my_double.should have_received(:value)
end