...

/

Making Tests Repeatable

Making Tests Repeatable

Let’s learn how to test functions that are not pure.

Testing impure functions

All of our tests are inside our functional core. In the core, calling a function with the same arguments will almost always result in the same output. The word “almost” is dangerous, though, because our whole strategy involves comparing our expectations to actual values. When we can’t have expectations from run to run, we must change our approach.

Timestamps and random numbers

Sometimes, functions are not perfectly pure. Functions that create timestamps or random numbers are famously challenging to test. We have both types of functions in our codebase. For example, recall the response test:

Press + to interact
test "a timestamp is added at build time", %{right: response} do
assert %DateTime{ } = response.timestamp
assert response.timestamp < DateTime.utc_now
end

We deal with that problem by changing the way we think about expectations. Rather than testing against an exact value, we make sure the timestamp is, in fact, a timestamp and that it’s before the present moment, utc_now.

Random numbers will be a little trickier. As we build our tests, we’ll dodge the random problem in most of them by building tests that restrict choices in one way or another. Let’s solve the easy problems first and save the toughest for last.

First, we need the typical test directives. We’ll create /test/question_test.exs and key this in:

Press + to interact
defmodule QuestionTest do
use ExUnit.Case
use QuizBuilders
end
...