I’ve run into a few challenges so far with testing Elixir processes. I would appreciate any insight you can provide.
Scenario: Process Communication
We generally build GenServer
processes in the form:
defmodule Whatever do
# Client API
def call_this(pid_or_name)
GenServer.call(pid_or_name, …)
end
# Server API
def handle_call(…)
# ...
end
end
Then let’s say I have code in another process that ends up doing something like:
defmodule Other do
def handle_call(…)
# …
Whatever.call_this(pid_or_name)
end
end
What’s the best way to test Other
without also needing to work with Whatever
or use mocking?
I can pass the module name to use into the process, but then I end up using apply()
everywhere internally and building parallel test APIs for everything I need to stub at some point. I’m open to better ideas.
Scenario: Periodic Task Processes
Let’s say that I want a process to periodically do some task. If it’s a simple process, I could use Process.sleep/1
in the main loop or :timer.send_interval/2
.
These are a pain to test, if the process manages this internally, but I’ve found that I can make a process respond to a message to trigger the task and eventually trigger that with :timer.send_interval/2
externally.
The move to GenServer
makes this worse. Process.sleep/1
is out because I don’t control the loop. :timer.send_interval/2
means I need to construct the internal message format, which feels wrong.
My best idea is to add a process that periodically calls the correct interface function for me. (And I’m back to apply()
.) Are there better ways?
Scenario: Kick-starting Well Tested Processes
In solving some of these issues, I find myself facing a new challenge. I have well tested processes that just need to be wired up to each other, set to receive periodic messages, or whatever. Now the question becomes: where do I put that code?
In the OTP application structure, where do I put the little bit of “glue” or “matchmaking” code that just needs to run on startup?