vrod

vrod

Very strange: handle_info not called unless I inspect pid messages

This is a very strange behavior I am seeing and I can’t to spot why it happens.
I am using Phoenix.PubSub to send messages to instances of a GenServer, something like this:

def start_link(%{source: source, from: _} = opts) do
    GenServer.start_link(__MODULE__, opts, name: :"for_#{source}")
end

@impl true
def init(%{source: source, from: _} = state) do
    PubSub.subscribe(:mypubsub, source, [])

    {:ok, state}
end

@impl true
def handle_info(message, %{from: from} = state) do
   IO.puts("HANDLE INFO")
   # side effects here
   send(from, "example")
   {:noreply, state}
end

However when I got to write tests for this, the HANDLE INFO sometimes is not called!

    test "things", %{test: test} do
      topic = "#{test}"

      {:ok, pid} = MyGenServer.start_link(%{source: topic, from: self()})

      :ok = PubSub.broadcast(:mypubsub, topic, "example message")

      # some assert_receive here

      # IO.inspect(Process.info(pid, :messages))
    end

I am very confused because I do not see the “HANDLE INFO” when I run my test! However, if I uncomment IO.inspect(Process.info(pid, :messages)) only THEN do I see the message. Can someone explain this? The module otherwise seems to be functioning as expected, it was only when I begin writing tests that I observed this strange thing.

Most Liked

dom

dom

Sounds like your test is returning before the GenServer receives/processes the message.

Calling a synchronous function on the GenServer (any function - can even be :sys.get_state(pid)) should ensure that the message has been processed before the test exits.

dom

dom

That won’t work because by the time you check the message may have been received already, so the message queue will be empty.

I wouldn’t assert on internal state personally. It doesn’t demonstrate that the component behaves correctly, and tying your test too closely to the implementation means it’ll break if you refactor the code (say, rename a variable in the gen_server state) even though the code still works fine. Same reason testing private methods or internal state in OOP is usually a bad idea.

Your original code where the test process sends its own pid as the from then does an assert_received looks great to me. You could even start_link a tiny Broadway pipeline in your test to get a realistic setup if you need to.

Just a tip though though - try assert_receive, rather than assert_received. The former waits for the message to arrive, the latter asserts that the message is already there and fails without waiting if it’s not. With assert_receive you won’t need the sys.get_state trick.

ityonemo

ityonemo

Note that this is not really a great way to write your tests.

You should probably write a “dump” function that relays the internal content of your genserver, and call that and assert that the internal state of your genserver is as expected.

I would also strongly question the existence of the “from” parameter in your genserver init, seems like a code smell.

Where Next?

Popular in Questions Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New

We're in Beta

About us Mission Statement