Patch.Mock.Value (patch v0.8.1) View Source
Interface for generating mock values.
In a test this module is imported into the test and so using this module directly is not necessary.
Link to this section Summary
Functions
Advances the given value.
Create a new Values.Callable
to be used as the mock value.
Create a new Values.Cycle
to be used as the mock value.
Guard that checks whether a value is a proper Values module
Generate the next return value and advance the underlying value.
Creates a special Values.Callable
to be used as a mock value.
Creates a special Values.Callable
to be used as a mock value.
Creates a new Values.Scalar
to be used as the mock value.
Creates a new Values.Sequence
to be used as a mock value.
Creates a special Values.Callable
to be used as a mock value.
Link to this section Types
Specs
t() :: Patch.Mock.Values.Callable.t() | Patch.Mock.Values.Cycle.t() | Patch.Mock.Values.Scalar.t() | Patch.Mock.Values.Sequence.t() | term()
Link to this section Functions
Specs
Advances the given value.
Sequences and Cycles both have meaningful advances, all other values types this acts as a no-op.
Specs
callable( target :: function(), dispatch :: Patch.Mock.Values.Callable.dispatch_mode() ) :: Patch.Mock.Values.Callable.t()
Create a new Values.Callable
to be used as the mock value.
When a patched function has a Values.Callable
as its mock value, it will invoke the callable
with the arguments to the patched function on every invocation to generate a new value to
return.
patch(Example, :example, callable(fn arg -> {:patched, arg} end))
assert Example.example(1) == {:patched, 1} # passes
assert Example.example(2) == {:patched, 2} # passes
assert Example.example(3) == {:patched, 3} # passes
Any function literal will automatically be promoted into a Values.Callable
unless it is
wrapped in a scalar/1
call.
patch(Example, :example, fn arg -> {:patched, arg} end)
assert Example.example(1) == {:patched, 1} # passes
assert Example.example(2) == {:patched, 2} # passes
assert Example.example(3) == {:patched, 3} # passes
callable/2
allows the test author to provide a dispatch_mode
of either :apply
or :list
.
When :apply
is used the function is called with the same arity of the patched function. When
:list
is used the function is always called with a single argument, a list of arguments to the
patched function.
patch(Example, :example, callable(fn a, b, c -> {:patched, a, b, c} end), :apply)
assert Example.example(1, 2, 3) == {:patched, 1, 2, 3} # passes
assert_raise BadArityError, fn ->
Example.example(:test)
end
Compare this with using list dispatch
patch(Example, :example, callable(fn
[a, b, c] ->
{:patched, a, b, c}
[a] ->
{:patched, a}
end, :list))
assert Example.example(1, 2, 3) == {:patched, 1, 2, 3} # passes
assert Example.example(1) == {:patched, 1} # passes
When multiple arity support is needed, use :list
dispatch.
Specs
cycle(values :: [term()]) :: Patch.Mock.Values.Cycle.t()
Create a new Values.Cycle
to be used as the mock value.
When a patched function has a Values.Cycle
as its mock value, it will provide the first value
in the cycle and then move the first value to the end of the cycle on every invocation.
Consider a function patched with cycle([1, 2, 3])
via the following code
patch(Example, :example, cycle([1, 2, 3]))
Invocation | Cycle Before Call | Return Value | Cycle After Call |
---|---|---|---|
1 | [1, 2, 3] | 1 | [2, 3, 1] |
2 | [2, 3, 1] | 2 | [3, 1, 2] |
3 | [3, 1, 2] | 3 | [1, 2, 3] |
4 | [1, 2, 3] | 1 | [2, 3, 1] |
5 | [2, 3, 1] | 2 | [3, 1, 2] |
6 | [3, 1, 2] | 3 | [1, 2, 3] |
7 | [1, 2, 3] | 1 | [2, 3, 1] |
We could continue the above table forever since the cycle will repeat endlessly. Cycles can
contain callable/1,2
, raise/1,2
and throw/1
mock values.
Guard that checks whether a value is a proper Values module
Specs
Generate the next return value and advance the underlying value.
Specs
raises(message :: String.t()) :: Patch.Mock.Values.Callable.t()
Creates a special Values.Callable
to be used as a mock value.
This callable ignores the arguments passed in and unconditionally raises a RuntimeError with the given message.
patch(Example, :example, raises("patched"))
assert_raise RuntimeError, "patched", fn ->
Example.example()
end
Specs
raises(exception :: module(), attributes :: Keyword.t()) :: Patch.Mock.Values.Callable.t()
Creates a special Values.Callable
to be used as a mock value.
This callable ignores the arguments passed in and unconditionally raises the specified exception with the given attributes.
patch(Example, :example, raises(ArgumentError, message: "patched"))
assert_raise ArgumentError, "patched", fn ->
Example.example()
end
Specs
scalar(value :: term()) :: Patch.Mock.Values.Scalar.t()
Creates a new Values.Scalar
to be used as the mock value.
When a patched function has a Values.Scalar
as its mock value, it will provide the scalar
value on every invocation
patch(Example, :example, scalar(:patched))
assert Example.example() == :patched # passes
assert Example.example() == :patched # passes
assert Example.example() == :patched # passes
When patching with any term that isn't a function, it will automatically be promoted into a
Values.Scalar
.
patch(Example, :example, :patched)
assert Example.example() == :patched # passes
assert Example.example() == :patched # passes
assert Example.example() == :patched # passes
Since functions are always automatically promoted to Values.Callable
, if a function is meant
as a scalar value it must be wrapped in a call to scalar/1
.
patch(Example, :get_name_normalizer, scalar(&String.downcase/1))
assert Example.get_name_normalizer == &String.downcase/1 # passes
Specs
sequence(values :: [term()]) :: Patch.Mock.Values.Sequence.t()
Creates a new Values.Sequence
to be used as a mock value.
When a patched function has a Values.Sequence
as its mock value, it will provide the first
value in the sequence as the return value and then discard the first value. Once the sequence
is down to a final value it will be retained and returned on every subsequent invocation.
Consider a function patched with sequence([1, 2, 3])
via the following code
patch(Example, :example, sequence([1, 2, 3]))
Invocation | Sequence Before Call | Return Value | Sequence After Call |
---|---|---|---|
1 | [1, 2, 3] | 1 | [2, 3] |
2 | [2, 3] | 2 | [3] |
3 | [3] | 3 | [3] |
4 | [3] | 3 | [3] |
5 | [3] | 3 | [3] |
We could continue the above table forever since the sequence will continue to return the last
value endlessly. Sequences can contain callable/1,2
, raise/1,2
and throw/1
mock values.
There is one special behavior of sequence, and that's an empty sequence, which always returns
the value nil
on every invocation.
If the test author would like to simulate an exhaustable sequence, one that returns a set number
of items and then responds to every other call with nil
, they can simply add a nil
as the
last element in the sequence
patch(Example, :example, sequence([1, 2, 3, nil])
Invocation | Sequence Before Call | Return Value | Sequence After Call |
---|---|---|---|
1 | [1, 2, 3, nil] | 1 | [2, 3, nil] |
2 | [2, 3, nil] | 2 | [3, nil] |
3 | [3, nil] | 3 | [nil] |
4 | [nil] | nil | [nil] |
5 | [nil] | nil | [nil] |
Specs
throws(value :: term()) :: Patch.Mock.Values.Callable.t()
Creates a special Values.Callable
to be used as a mock value.
This callable ignores the arguments passed in and unconditionally throws the given value.
patch(Example, :example, throws(:patched))
assert catch_throw(Example.example()) == :patched