Packages

A set of helpers for testing `timer`-based code, inspired by Jest.

Current section

Files

Jump to
postpone lib postpone.ex
Raw

lib/postpone.ex

defmodule Postpone do
@moduledoc """
A set of helpers for testing `timer`-based code, inspired by [`Jest`](https://jestjs.io/docs/en/timer-mocks).
## Why?
Sometimes things need to be postponed. Whether it's calling a function or sending a message, Erlang's `timer` module has a function for that.
However, this creates a haeadache when it comes to testing. Time-based test are often fragile, tricky to write and [mocking `timer` with popular
tools is even trickier](https://github.com/eproxus/meck#caveats).
`Postpone` offers a solution for this problem. It allows a user to postpone certain actions, using trusted `timer` under the hood,
but at the same time offers a way to mock and control timers inside test cases.
## Usage
Use `Postpone` in your code:
defmodule Foo do
def send_me_a_greeting do
Postpone.send_after(:hello, self(), 1000)
end
end
Then test it:
defmodule FooTest do
use ExUnit.Case
use Postpone.Mock
test "greeting" do
with_timers_mock do
Foo.send_me_a_greeting()
run_all_timers()
assert_received :hello
end
end
end
"""
@doc """
Sends a `msg` to `pid` after `time`.
"""
@spec send_after(term(), pid(), non_neg_integer()) :: {:ok, reference()} | {:error, term()}
def send_after(msg, pid, time) do
:timer.send_after(time, pid, msg)
end
@doc """
Applies `fun` or `fun` in `mod` with `args` after `time`.
"""
@spec apply_after(fun() | mfa(), non_neg_integer()) :: {:ok, reference()} | {:error, term()}
def apply_after(fun, time) when is_function(fun) do
apply_after({:erlang, :apply, [fun, []]}, time)
end
def apply_after({mod, fun, args}, time) do
:timer.apply_after(time, mod, fun, args)
end
end