I am implementing Server Sent events in a phoenix project. This is easy enough to do by using plug directly. I am following this gist.
However I am not sure of the best way to test such an endpoint. I could use HTTPoison Async Requests and connect to the test server but I would rather just test the conn object using Plug.Test
or Phoenix.ConnTest
. Is this possible are there any examples out there on how to do this.
2 Likes
I researched this a bit, and it’s indeed far from being trivial. From what I found, when we call Plug.Conn.chunk(conn, chunk)
, plug calls conn.adapter.chunk
. From there, the chunk should be sent to the server (e.g. cowboy) for further handling. The conn is not aware of the chunk anymore.
In tests, the adapter is Plug.Adapters.Test.Conn
, and its chunk function reply with {:ok, payload}
. It doesn’t really matter what the payload is, because Plug.Conn.chunk
is probably called from a process that is not going to finish / return this state in any way. This process communicate messages back to the client forever, in a loop.
So, in the case of the test, I see no way to get the chunk out of the process, at least not in a “conventional” way. So, I tried to solve this by mocking Plug.Conn.chunk
to send a the chunk to the test process, and there I assert_receive
for it. You can see an example here. It’s not the cleanest solution, I know… What do you think?
1 Like
I tried to get the method by @Nagasaki45 but I could not get it to work. I also tried mocking Plug.Conn.chunk
which also didn’t work for me I ended up doing this.
I created a function with minimal side effects and used that to run the chunking.
defmodule MyApp.ControllerUtils do
use MyaAppWeb, :controller
@callback chunk_to_conn(map(), String.t()) :: map()
def chunk_to_conn(conn, current_chunk) do
conn |> chunk(current_chunk)
end
end
Now in my controller, i call chunk_to_conn(conn, current_chunk)
which I can mock in test to give me the chunks like this
In my test support
Mox.defmock(MyAppWeb.ControllerUtilsMock, for: MyAppWeb.ControllerUtils)
And in my test
defmodule MyApp.MyTest do
import Mox
defp chunked_response_to_state(chunk, pid) do
current_chunk = Agent.get(pid, &Map.get(&1, :chunk_key))
Agent.update(pid, &Map.put(&1, :csv, current_chunk <> chunk))
end
setup do
MyApp.ControllerUtilsMock
|> stub(:chunk_to_conn, fn _, chunk -> chunk |> chunked_response_to_state(agent_pid) end)
{:ok, %{agent_pid: agent_pid}}
end
test "my test", state do
build_conn.get(somepath)
whole_chunks = Agent.get(state.agent_pid, &Map.get(&1, :chunk_key))
end
end
1 Like