leighshepperson

leighshepperson

Stubr v1.4.0

A large update has been made to Stubr including the introduction of spies and a large collection of functions like called_with?, called_once?, returns?, etc… The full documentation is available in hexdocs.

About Stubr

Stubr is a set of functions helping people to create stubs and spies in Elixir.

In Elixir, you should aim to write pure functions. However, sometimes you need to write functions that post to external API’s or functions that depend on the current time. Since these actions can lead to side effects, they can 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)
:ok
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

Examples

Stubs

Stubs can be created using stub!/1 and stub!/2:

iex> stub = Stubr.stub!([foo: fn -> :bar end])
iex> stub.foo()
:bar

iex> stub = Stubr.stub!([foo: fn _ -> :bar end], call_info: true)
iex> stub.foo(:baz)
iex> stub |> Stubr.called_with?(:foo, [:baz])
true

The input to these functions is a keyword list of function names
(expressed as atoms) and their implementations (expressed as
anonymous functions):

[function_name: (...) -> any()]

Additionally, takes an optional keyword list to configure the stub.

Options

The options available to stub!/2 are:

  • :module - when set, if the module does not contain a function
    defined in the keyword list, then raises an UndefinedFunctionError

  • :auto_stub - when true and a module has been set, if there
    is not a matching function, then defers to the module function
    (defaults to false)

  • behaviour - when set, raises a warning if the stub does not
    implement the behaviour

  • call_info - when set, if a function is called, records the input
    and the output to the function. Accessed by calling
    Stubr.call_info(stub, :function_name)
    (defaults to false)

Spies

Spies can be created using spy!/1:

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

Random numbers

It is easy to 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, it is just used to make 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, we can use Stubr.stub! to stub the Timex.now/0 function in the Timex module. However, we also want the stub to defer to the original functionality of the Timex.before?/2 function. 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 that return different values dependant 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

Showing Posts 1 to 1

andre1sk

andre1sk

Congrats looks great!

— All posts loaded —

Where Next? Top

Trending in Announcing Top

woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. https://github.com/woylie/doggo Features Unstyled Phoenix components....
New
GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New
CodeSync
:microphone: ElixirConf 2026 - Call for Talks is open! We’re heading to Chicago :united_states: :round_pushpin: In person + virtual :d...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews