Search code examples
elixirintegration-testingphoenix-frameworkphoenix-channelsex-unit

How to read new Phoenix channel instance's state in Channel test case?


I have the following phoenix channel that handle an incoming message, broadcasts it and then updates the channel instance's socket state:

defmodule MyApp.MyChannel do
  use MyApp.Web, :channel

  def join("topic", _payload, socket) do
    {:ok, socket}
  end

  def handle_in("update", %{"new_number" => number_}, socket) do
    broadcast socket, "update", %{"new_number" => number_} 
    {:noreply, assign(socket, :current_number, number_)}
  end
  ...
end

I am trying to test the behavior of the handle_in("update", ...) function via this test case:

test "should broadcast new number and update the relevant instance's socket state", %{socket: socket} do
  push socket, "update", %{"new_number" => 356}
  assert_broadcast "update", %{"new_number" => 356}
  ## This is testing against the old state
  ## which is going to obviously fail
  assert socket.assigns[:current_number] == 356  
end

The issue here is that I can't find a way to get the new updated socket state inside the test case.

  • There is no assert_socket_state function in the Phoenix.ChannelTest module and I can't find any function allowing to get the newest socket state

  • I thought about defining a handle_call or a handle_info that returns the socket state but this means that I will have to get the channel's pid in order to call them.

  • I thought about defining a handle_in for this purpose but I don't want to put in my channel with an introspection tool that is going to be available in production.

How can I get the updated socket in the test case, after pushing the message?


Solution

  • The socket state contains a channel_pid entry that basically contains the pid of the channel.

    The previous combined with the :sys.get_state/1 function which takes the pid of a GenServer and returns its most recent state is the key!

    Example, given a socket state inside a test case:

    :sys.get_state(socket.channel_pid).assigns[:current_number]
    

    Credit goes to Dogbert for their comment on the question.