When writing tests in Elixir using ExUnit, you may want to run the same test logic with multiple input valuesâmuch like data providers in PHPUnit. While ExUnit doesnât have a built-in equivalent, you can achieve the same result by dynamically generating test cases using for or Enum.each.
In this post, weâll look at how to implement parameterized tests and why unquote is essential when doing so inside macros.
Basic parameterized test with Enum.each
You can group multiple test cases into a single test using Enum.each:
test "adds numbers correctly" do
[
{1, 2, 3},
{5, 5, 10},
{-1, 1, 0}
]
|> Enum.each(fn {a, b, expected} ->
assert a + b == expected
end)
end
This works well, but all cases are part of the same test. If one fails, the whole test fails.
generating separate tests with for
To create separate test cases (so that each one is reported independently by the test runner), you can use a for comprehension with the test macro:
for {a, b, expected} <- [
{1, 2, 3},
{5, 5, 10},
{-1, 1, 0}
] do
test "adding #{a} + #{b} gives #{expected}" do
assert unquote(a) + unquote(b) == unquote(expected)
end
end
This pattern creates multiple independent tests, one for each set of values.
Why unquote
Is Required
Elixir macros work by manipulating quoted code (abstract syntax trees). When you use the test macro inside a loop, youâre generating code at compile time, not runtime. That means the variables a, b, and expected in the body of the test are part of a quoted block.
To insert the actual values from the loop into the generated test, you need to use unquote. Without it, the values would remain as variable references and wouldnât be interpolated properly, leading to incorrect or failing tests.
For example, the line:
assert unquote(a) + unquote(b) == unquote(expected)
inserts the concrete values from the loop directly into the generated test, resulting in:
test "adding 1 + 2 gives 3" do
assert 1 + 2 == 3
end
This ensures the test behaves as expected.
Conclusion
While ExUnit doesnât have built-in data providers like PHPUnit, Elixirâs powerful macro system and list comprehension features make it easy to write parameterized tests. Just remember to use unquote to inject loop variables into your dynamically generated test code.
This approach keeps your tests concise, expressive, and fully integrated with ExUnitâs reporting and tooling.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.