Using `Process.register` and getting "process attempted to call itself"

I’m getting an error that I’m not sure how to deal with when using Process.register to make an assertion against a received message. Does anyone know how I can fix this? I’ve been trying to fix this for about 5 hours.

There’s sample code below, but for more, check:

** (EXIT) process attempted to call itself
defmodule Module do
  def save(model) do
    GenServer.cast(:eidetic_eventstore_adapter, {:record_event, event})
  test "It can loop uncommitted events and delegate them to the adapter" do
    Process.register(self(), :eidetic_eventstore_adapter)
    user = %Example.User{} =
      Example.User.register(forename: "Darrell", surname: "Abbott")

    for event <- user.meta.uncommitted_events do
      assert_received {:"$gen_call", {:record, event}}
1 Like

It’s this line:

You call in your test which calls the the save function, which ends up doing an Agent.get :eidetic_eventstore_adapter which is of course the current test process. Agent.get is a

Relatedly, why are you manually calling Agent.start_link inside a supervisor init?

It’s actually the following that is causing the problem:

for event <- model.meta.uncommitted_events do                                                                                  , {:record, event})                                                                       

I can remove the Agent code and I still get the same error.

As for the Agent, it’s to store the subscribers. I wasn’t sure where else to store them, though I have removed that code for the time being.

This worked previously, when I was using cast rather than call.

Can I not use Process.register to test calls, is there another way to do this?

Thanks for taking the time to look at this, @benwilson512.
It’s got me pretty stumped!

When you do a call, the function awaits the reply. This means that the test will wait for a reply from itself, but since it’s waiting, it can never send it - you have a deadlock. That’s why Elixir’s GenServer will bail out of this situation early.


Thanks for the response, @michalmuskala. That makes sense. How would you test a call, then?

Here you could run it in a separate process spawn_link(fn -> end). You may want to monitor that process as well to ensure the test doesn’t return before it’s done.

Testing the private GenServer API is a strange and brittle thing to do though. If a module A is a client of module B, and B is a GenServer, module A should never call directly. It should call a function in B (e.g. which then does a Then you’re free to replace B with an alternate module C during your tests that has the same API but isn’t a GenServer - for instance it could store events in an Agent which the test process can query after the save() to check what was saved. See


Call is synchronous, so you can test the return value easily enough in your test. For async calls such as cast, if they respond with a message you can use ExUnit’s assert_receive. Otherwise you need to somehow test the GenServer internal state data changes.

I also understand that the way it works, and this seems to be held up by some simple testing here, that given that the messages sent by call/cast are handled in the same process they are therefore serialized in that process by GenServer’s receive loop. So you can do a cast, then a call to check the changed value which will block in your test as it is sync, and be run after the cast’d call in the GenServer giving you a predictable order of events.

If there is no return value and no internal state change, then you don’t need to test it :wink: So I think the above handles all useful cases?

1 Like

I wouldn’t test the call directly. I would start the real process and test the public interface of the behaviour module - something more like an integration test, than a pure unit test of a single function.

If the logic inside the process is so complex, that I feel like I need to test it on a lower level, I usually go for defining a separate module for the state of the gen server exporting pure functions that manage it. Then I can test it separately, and call those functions from inside the GenServer.

1 Like

@dom Thanks for that link, really interesting read.

Thanks for everyone’s responses. I ended up implementing a “GenServer” event store, as well as a MongoDB, which I use in the tests now.

Thanks for helping out, it’s been fun learning from your comments :thumbsup: