How to setup data when writing test in Elixir


Story
Recently, I decided to update several test files that uses the same test setup for their data. Mostly the code was written with the same structure
# assuming you are using ExMachina package
inserted_at = DateTime.utc_now() |> DateTime.add(-30, :day) |> DateTime.to_naive
organization = insert(:organization, inserted_at: inserted_at)
In the above code, I want all my test setup to have this kind of data but in order for me to do that I need to repeat this code in every unit test.
test "test 1" do
inserted_at = DateTime.utc_now() |> DateTime.add(-30, :day) |> DateTime.to_naive
organization = insert(:organization, inserted_at: inserted_at)
end
test "test 2" do
inserted_at = DateTime.utc_now() |> DateTime.add(-30, :day) |> DateTime.to_naive
organization = insert(:organization, inserted_at: inserted_at)
end
As much as possible we don’t want repeated code just like the above.
First Solution
ExUnit has a way how to conveniently setup your whole test to have the same setup. The easiest way is to make a private function and invoke it before your tests or inside your describe block.
setup [:setup_organization]
test "test 1", %{organization: organization} do
# do test 1 logic here
end
test "test 2", %{organization: organization} do
# do test 2 logic here
end
defp setup_organization(context) do
inserted_at = DateTime.utc_now() |> DateTime.add(-30, :day) |> DateTime.to_naive
organization = insert(:organization, inserted_at: inserted_at)
{:ok, Map.put(context, :organization, organization)
end
Now, this one seems already nice. We manage to DRY the logic for setting up the organization data, call setup/1
and put setup_organization
to one of the setups.
But here is the thing, I still need to update every test file that uses this same logic. I do not want to copy all of these code since it is going to create repeated codes again.
Second Solution
In order for me to have this setup to all test files that needs this, I decided to create a helper module that implements the same context function we created in one file.
defmodule TestHelper do
@moduledoc false
@spec setup_organization(context :: map()) :: {:ok, map()}
def setup_organization(context) do
inserted_at = DateTime.utc_now() |> DateTime.add(-30, :day) |> DateTime.to_naive
organization = insert(:organization, inserted_at: inserted_at)
{:ok, Map.put(context, :organization, organization)
end
end
setup [:setup_organization]
test "test 1", %{organization: organization} do
# do test 1 logic here
end
test "test 2", %{organization: organization} do
# do test 2 logic here
end
The question is, how can we call the helper function in another module?
Instead of calling the :setup_organization
context function like the above we need to tweak how do we call it.
# This is where the magic happens
setup [{TestHelper, :setup_organization}]
test "test 1", %{organization: organization} do
# do test 1 logic here
end
test "test 2", %{organization: organization} do
# do test 2 logic here
end
This helper context function can now be accessible to every test files that needs this.
Happy Coding!
References
Subscribe to my newsletter
Read articles from Pau Riosa directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Pau Riosa
Pau Riosa
🎥 Youtuber 💻. Software Developer 📕 Blogger I am software developer who has passion for career development and software engineering. I love building stuff that can help people all around the world. Let's connect and share that idea!