Is it possible to have elixir running with a custom node name during tests?

In Erlang, using “Common Test” framework, each test execution has a unique node name allowing a higher degree of resources isolation. Is it possible to achieve the same in elixir with ExUnit?

ExUnit tests may run async to each other, so afaik there is no built in support for something like that. Though I guess nothing would prevent you from creating a case template, which restarts distribution each time and assigns a unique name to the node.

2 Likes

Hmmm. I’m not sure how I could restart the distribution, but I’ll look into it! Thank you!

So that would be with :net_kernel.start/1 and :net_kernel.stop/0 right?

Or Node.start/3 and Node.stop/0

1 Like

Neat! Now I just have to find a way to start epmd when I run mix test, because apparently it is not being started by default.

Let me know if you do find a way. I‘ve resorted to raising an error in the tests I use distribution at the moment.

I didn’t know those even existed :smiley:

@leorodriguesrj To start epmd add {_, 0} = System.cmd("epmd", ["-daemon"]) before ExUnit.start() in test_helper.exs

1 Like

I came up with this :point_down:t3: work in progress!

# Starting "epmd"
epmd_path = System.find_executable("epmd")
port = Port.open({:spawn_executable, epmd_path}, [ ])
os_pid = Keyword.get(Port.info(port), :os_pid)

# Configuring a "shutdown hook" to stop epmd after everything is done.
# (I'm not particularly proud of this implementation, honestly)
System.at_exit(fn _ -> System.shell("kill -TERM #{os_pid}") end)

# This is a bit of an exageration just to compute a name
{:ok, now} = DateTime.now("Etc/UTC")
now = DateTime.to_unix(now)
cipher = '#{now}'
  |> Enum.map(fn c -> c + 49 end)
  |> List.to_string()

# Turn this node into a distributed node
{:ok, _} = Node.start(String.to_atom("#{cipher}@testhost"))

# Finally, start the tests
ExUnit.start()

For some reason, I was unable to make correct use of Port.close/1, so I used the kill command to stop epmd. Obviously, this implementation will fail in windows.

I wouldn’t use clustering as a strategy for resource segregation. Certainly “actually testing clustering” using test-time clusters (for example using the :slave module) is a thing. But for resource segregation I think “recommended way” to do this is to do it in one node, and take advantage of something like Mox to mock out the contended resource, and in your mock, stub out calls to a process started using ExUnit.start_supervised – this process can be stateful and will automatically have a lifetime that matches the lifetime of your test. Mox is already “a resource that is sharded based on tests” so that could make what you are trying to do WAY easier.

You could also dig deeper and use the Process dictionary, :$caller and :$ancestor keys to manually build resource-shardable subsystems, and make it so that each test has its own checkout of your resource. I built a library around this concept but I don’t currently recommend it.

1 Like

Well I think I can consider this issue solved. But to further elucidate on the matter to @ityonemo, I’m currently building an application on top of Mnesia and whenever I ran the tests the database was being scrambled because Mnesia was always running on the same node.

I had some code built in erlang before using Common Test Framework. In ct, every time you run the tests, you run on a different node and the database is automatically isolated. I was trying to achieve a similar behavior in Elixir with ExUnit.