Search code examples
socketstestingtimeoutelixirphoenix-framework

Configure channel test timeout phoenix


I have a channel which I'm using to push messages to the client when carrying out some kind of sync:

defmodule AppWeb.SyncChannel do
  use AppWeb, :channel

  def join("sync:database", _payload, socket) do
    send(self(), {:sync, :database})

    {:ok, socket}
  end

  def handle_info({:sync, :database}, socket) do
    Process.sleep(2000)
    push(socket, "one", %{})
    Process.sleep(2000)
    push(socket, "two", %{})
    Process.sleep(2000)
    push(socket, "three", %{})

    {:noreply, socket}
  end
end

I have a test for this channel:

defmodule AppWeb.SyncChannelTest do
  use AppWeb.ChannelCase, async: false

  alias AppWeb.SyncChannel

  describe "sync:database" do
    setup do
      {:ok, _, socket} = subscribe_and_join(socket(), SyncChannel, "sync:database")

      {:ok, socket: socket}
    end

    test "socket pushes 3 messages", %{socket: _socket} do
      assert_push("one", %{})
      assert_push("two", %{})
      assert_push("three", %{})
    end
  end
end

But when I run the tests, I am getting the error:

Compiling 1 file (.ex)
...

  1) test sync:database socket pushes 3 messages (AppWeb.SyncChannelTest)
     test/app_web/channels/sync_channel_test.exs:13
     ** (exit) exited in: GenServer.call(#PID<0.478.0>, :socket, 5000)
         ** (EXIT) time out
     stacktrace:
       (elixir) lib/gen_server.ex:830: GenServer.call/3
       (phoenix) lib/phoenix/test/channel_test.ex:360: Phoenix.ChannelTest.join/4
       test/app_web/channels/sync_channel_test.exs:8: AppWeb.SyncChannelTest.__ex_unit_setup_1/1
       test/app_web/channels/sync_channel_test.exs:1: AppWeb.SyncChannelTest.__ex_unit__/2

.

Finished in 5.2 seconds
5 tests, 1 failure

Randomized with seed 119011

How can I configure the channel timeout in my tests so that handle_info function is able to run for more than the default 5 seconds.

I have tried looking to configure this in the config/ files, without joy and also in the app_web/channels/user_socket.ex, but again I can't find anywhere specify a timeout


Solution

  • Phoenix.ChannelTest.join calls Phoenix.Channel.Server.socket/1 which makes a GenServer call to the channel with no configurable timeout to get the underlying socket from the GenServer's state. I believe since you send a message to self from your join function, that message is processed by the GenServer before the test code is able to get the socket value and since that call has the default timeout of 5 seconds, you get this timeout error.

    A workaround for this would be to slightly delay the send to self using Process.send_after/3:

    Process.send_after(self(), {:sync, :database}, 100)
    

    You'll also need to increase the timeout of assert_push calls as the timeout defaults to 100ms while your messages arrive upto around 6 seconds later.

    assert_push ..., ..., 10000
    

    Again, Process.send_after/3 is just a workaround. Someone more knowledgeable might be able to provide a real solution.