defmodule SmartCity.TestHelper do @moduledoc """ Common utilities for use in test cases """ # no_return() is because eventually() catches ExUnit.AssertionError @type asserting_function :: (() -> no_return() | boolean()) @doc """ Helper function for asynchronous testing - Repeats the given block (zero-arity function) until a timeout occurs or it returns a truthy value. - Catches ExUnit assertions and continues to try the block - Re-runs block one last time if it times out so assertions are run and throw helpful diffs, etc. """ @spec eventually(asserting_function(), integer, integer) :: :ok | none() def eventually(function, dwell \\ 2_000, max_tries \\ 5) do case Patiently.wait_for( wrap_assertions_as_falsey(function), dwell: dwell, max_tries: max_tries ) do :ok -> :ok _ -> case function.() do false -> raise ExUnit.AssertionError, "no assertion was made in the eventually block but the block evaluated as false" result -> result end end end defp wrap_assertions_as_falsey(function) do fn -> try do function.() rescue _e in [ExUnit.AssertionError, MatchError] -> false end end end end