ExStub
ExStub
provides an easy way to stub a module and record the function calls on it.
Installation
Add ex_stub
to your deps in mix.exs
as a development dependency.
def deps do
[{:ex_stub, "~> 0.1.0", only: :test}]
end
Usage
If you have a module in your original application like:
defmodule OriginalModule do
def process(param), do: :original_process
def another_method, do: :original_method
end
You can quickly create a stub copy of this module using defstub
use ExStub
defstub MyStub, for: OriginalModule do
def process(true), do: :stubbed1
def process(false), do: :stubbed2
def process(1), do: :stubbed3
end
Now you can pass around MyStub
instead of OriginalModule
.
When you invoke method from the created MyStub
, if the method was stubbed it will call the stubbed version.
Else the original version will be called.
MyStub.process(true) # returns :stubbed1
MyStub.process(false) # returns :stubbed2
MyStub.process(1) # returns :stubbed3
MyStub.process(20) # returns :original_process
MyStub.another_method # returns :original_method
Notice that Since we did not stub another_method
, calling it on MyStub
returns the original implementation.
Also when calling MyStub.process(20)
the original implementation is called since it failed pattern matching with our stub version of the method.
As a safety procedure, if you try to stub a method that is not found in the original module. ExStub will throw a compilation error telling you about the unexpected stubbed method.
defstub MyStub, for: OriginalModule do
def new_method(), do: :stubbed1
end
The following error will be thrown
** (RuntimeError) Cannot provide implementations for methods that are not in the original module
The def `{:new_method, 0}` is not defined in module `OriginalModule`
Recording method calls
All the functions called on the defstub
created module will be recorded.
To get all the functions calls on YourModule
module
ExStub.Recorder.calls(YourModule)
To get all the :the_method
function calls on YourModule
ExStub.Recorder.calls(YourModule, :the_method)
Alternativey, you can use assert_called
in your unit tests:
The syntax is assert_called ModuleName.function_name(params)
# No parameters
assert_called ModuleName.function_name
# nil passed
assert_called ModuleName.function_name(nil)
# multiple parameters
assert_called ModuleName.function_name(1, 2)
Some more examples
MyStub.process(1)
# Passes since we called the function with [1]
assert_called MyStub.process(1)
# Fails since the parameters dont match
assert_called MyStub.process(1, 2)
# Fails since we did not call `another_method`
assert_called MyStub.another_method