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?

First 2 of 2 Posts Switch mode

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

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
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
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
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
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement