JEG2

JEG2

Author of Designing Elixir Systems with OTP

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?

Showing Posts 1 to 2

gregvaughn

gregvaughn

What I found in my first OTP learning app was the code telling me to push identification of collaborators into start_link calls and store them in a GenServer’s state. Those in turn the supervisor can read from config data in production. But in the test, you can start_link Other and pass in self as the pid to substitute in as a callback process. Then you can assert_receive or similar.

kujua

kujua

Author of Erlang and Elixir for Imperative Programmers

@JEG2 Can feel your pain.

Let me answer this from my perspective on a more general level:

(1) There is no rule that says have your handle_call in this function. Your scenario 1 calls the other server. What is the code before and after the call. What do you try to test? If it is just a call to Whatever than you need to test Whatever - perhaps having the code for handling the call in the module that can be tested easily.

(2) Mocking: It has its disadvantages, but should not be ruled out all the time. I have seen lots of abuse of mocking, but also useful case. Let’s assume that - again scenario 1 - the call to Whatever returns a critical state, then you would like to mock it to create different use cases - error, timeouts, different states. In my opinion, the call for some sort of dependency injection can lead to the same abuse than mocking.

(3) Kick-starting … Are the processes supervised? Then the code - I guess matchmaking means to pass an initial state - would be in the code that starts the children.

— All posts loaded —

Where Next? Top

Trending in Questions Top

water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New

Other Trending Topics Top

KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews