Description
In functional languages you should write pure functions. However, sometimes we need functions to call external API’s that affect the state of the system. So these functions are impure. In non-functional languages you create mocks to test expectations. For example, you might create a mock of a repository and the test checks it calls the save function. You are testing a side effect. This is something you should avoid in functional languages.
Instead of mocks we should use stubs. Mocking frameworks tend to treat them as interchangeable and this makes it hard to tell them apart. So it is good to have a simple definition. Quoting Martin Fowler:
Stubr alternatives and similar packages
Based on the "Testing" category.
Alternatively, view Stubr alternatives based on common mentions on social networks and blogs.
-
bypass
Bypass provides a quick way to create a custom plug that can be put in place instead of an actual HTTP server to return prebaked responses to client requests. -
mix_test_interactive
Interactive watch mode for Elixir's mix test. https://hexdocs.pm/mix_test_interactive/ -
mecks_unit
A simple Elixir package to elegantly mock module functions within (asynchronous) ExUnit tests using Erlang's :meck library
CodeRabbit: AI Code Reviews for Developers

Do you think we are missing an alternative of Stubr or a related project?
Popular Comparisons
README
Stubr
Stubr is a set of functions helping people to create stubs and spies in Elixir.
About
Elixir is a functional language, so you should aim to write pure functions. However, sometimes you need to call external API’s or check the current time. Since these actions can have side effects, they make it harder to unit test your system.
Stubr solves this problem by taking cues from mocks and explicit contracts. It provides a set of functions that help people create "mocks as nouns" and not "mocks as verbs":
iex> stub = Stubr.stub!([foo: fn _ -> :ok end], call_info: true)
iex> stub.foo(1)
iex> stub |> Stubr.called_once?(:foo)
true
iex> spy = Stubr.spy!(Float)
iex> spy.ceil(1.5)
iex> spy |> Stubr.called_with?(:ceil, [1.5])
true
iex> spy |> Stubr.called_twice?(:ceil)
false
Installation
Stubr is available in Hex, the package can be installed as:
Add stubr
to your list of dependencies in mix.exs
:
def deps do
[{:stubr, "~> 1.5.1", only: :test}]
end
Developer documentation
Stubr documentation is available in hexdocs.
Examples
Random numbers
Use Stubr.stub!
to set up a stub for the uniform/1
function in the :rand
module. Note, there is no need to explicitly set the module option, however, it is useful to do so because it makes sure the uniform/1
function exists in the :rand
module.
test "create a stub of the :rand.uniform/1 function" do
rand_stub = Stubr.stub!([uniform: fn _ -> 1 end], module: :rand)
assert rand_stub.uniform(1) == 1
assert rand_stub.uniform(2) == 1
assert rand_stub.uniform(3) == 1
assert rand_stub.uniform(4) == 1
assert rand_stub.uniform(5) == 1
assert rand_stub.uniform(6) == 1
end
Timex
As above, use Stubr.stub!
to stub the Timex.now/0
function in the Timex
module. However, we also want the stub to act as a transparent proxy over the Timex
module for all non-stubbed functions. To do this, we just set the module
option to Timex
and the auto_stub
option to true
.
test "create a stub of Timex.now/0 and defer on all other functions" do
fixed_time = Timex.to_datetime({2999, 12, 30})
timex_stub = Stubr.stub!([now: fn -> fixed_time end], module: Timex, auto_stub: true)
assert timex_stub.now == fixed_time
assert timex_stub.before?(fixed_time, timex_stub.shift(fixed_time, days: 1))
end
HTTPoison
In this example, we create stubs of the functions get
and post
in the HTTPoison
module and make them return different values based on their inputs:
setup_all do
http_poison_stub = Stubr.stub!([
get: fn("www.google.com") -> {:ok, %HTTPoison.Response{body: "search", status_code: 200}} end,
get: fn("www.nasa.com") -> {:ok, %HTTPoison.Response{status_code: 401}} end,
post: fn("www.nasa.com", _) -> {:error, %HTTPoison.Error{reason: :econnrefused}} end
], module: HTTPoison)
[stub: http_poison_stub]
end
test "create a stub of HTTPoison.get/1", context do
{:ok, google_response} = context[:stub].get("www.google.com")
{:ok, nasa_response} = context[:stub].get("www.nasa.com")
assert google_response.body == "search"
assert google_response.status_code == 200
assert nasa_response.status_code == 401
end
test "create a stub of HTTPoison.post/2", context do
{:error, error} = context[:stub].post("www.nasa.com", "any content")
assert error.reason == :econnrefused
end
Links
TDD in functional languages using stubs: https://www.infoq.com/presentations/mock-fsharp-tdd
Mark Seemann's blog post about the difference between Mocks and Stubs in the context of commands and queries.