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
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
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?