Testing an elixir function using Mock

First, here’s a stateful Agent that holds a counter and a simple logic that forces it to return the same value several times, before starting to return random values afterwards. Like this:

defmodule NanoidMock do
  @moduledoc ~S"""
  Mocks Nanoid value producer with a simple counter.
  Initialize with `start_link(0)` to reproduce the following:
  For the first 4 invocations of `get_nano_id` the counter
  will move from 0 to 3 and will always return 0.
  After 4 invocations of `get_nano_id` (when the counter
  reaches 4) and onwards, it will return a random number.
  """

  use Agent

  # Initialize with a non-negative integer counter.
  def start_link(val) when val >= 0 do
    Agent.start_link(fn -> val end, name: __MODULE__)
  end

  def stop(), do: Agent.stop(__MODULE__)

  def clear(), do: Agent.update(__MODULE__, fn(_) -> 0 end)

  def get_nano_id() do
    new_val = Agent.get_and_update(__MODULE__, fn(x) -> {x, x + 1} end)
    case div(new_val, 4) do
      0 -> 0
      _ -> :rand.uniform(1_000_000)
    end
  end
end

Then let’s compose a test class:

defmodule NanoidTestWithMock do
  def get_nano_id(), do: NanoidMock.get_nano_id()

  def upi_generate() do
    upi = "A#{get_nano_id()}C"

    case get_product_with_upi(upi) do
      nil -> upi
      _   -> upi_generate()
    end
  end

  defp get_product_with_upi(upi) do
    case upi do
      "A0C" ->
        IO.puts "Duplicated UPI=A0C. Retrying."
        %{error: "product already exists"}

      _ ->
        nil
    end
  end

  def test() do
    NanoidMock.start_link(0)
    upi = upi_generate()
    NanoidMock.stop()
    IO.puts "Successfuly got unique UPI=#{upi}"
  end
end

Put these modules in a single Elixir file and then run NanoidTestWithMock.test() in an iex session.

(Or add the above expression at the bottom of the file and just tell Elixir to run it.)

You should see something like this:

Duplicated UPI=A0C. Retrying.
Duplicated UPI=A0C. Retrying.
Duplicated UPI=A0C. Retrying.
Duplicated UPI=A0C. Retrying.
Successfuly got unique UPI=A394747C

Hope that helps.

EDIT: As for mocking the Nanoid library itself, I’ll leave that exercise to you.