I’ve got a Genserver that writes messages to stdout. I can’t figure out how to test using capture_io. Documentation says By default, capture_io replaces the group_leader (:stdio) for the current process. Perhaps there is a Process.group_leader call that will make this work, but I haven’t been able to find it. Can anyone point me to a solution?
Here’s a failing test script…
#!/usr/bin/env elixir
ExUnit.start()
defmodule MyGenServer do
use GenServer
def start_link(default \\ []) do
GenServer.start_link(__MODULE__, default, name: __MODULE__)
end
def init(initial_state) do
{:ok, initial_state}
end
def handle_call(:sayhi, _caller, state) do
IO.puts("HI")
{:reply, :ok, state}
end
end
defmodule MyGenServerTest do
use ExUnit.Case
import ExUnit.CaptureIO
test "GenServer responds to :sayhi call by writing 'HI' to stdout" do
{:ok, pid} = MyGenServer.start_link()
assert capture_io(fn -> GenServer.call(pid, :sayhi) end) == "HI\n"
end
end
I could make it work with capture_log + import ExUnit.CaptureLog + require Logger inside both modules + actually using Logger.debug (or any other level) and notIO.puts but curiously enough I wasn’t able to make it work with capture_io like in your original script. Also tried with with_io but no dice again. Interesting.
Ya, this was the closest I could find to an answer which is probably something you’ve already come across at this point. I may be brain-farting due to fatigue but I couldn’t quite get it to work with GenServer. I did run into this a while back so I’m pretty curious. It’s not in code I have access to anymore but I think I ended up just going with the repeated advice I keep coming across which is to test behaviour by asserting on the callbacks directly then I guess you can just assert_receive to test the API layer? I honestly don’t quite remember what I did. Sorry if this isn’t very helpful.
The problem is that you have to set the group leader first, which doesn’t seem possible with the way that capture_io/1 currently works:
# group leader is set before starting the GenServer, so this works
assert ExUnit.CaptureIO.capture_io(fn ->
{:ok, pid} = MyGenServer.start_link()
GenServer.call(pid, :sayhi)
GenServer.stop(pid)
end) == "HI\n"
I wonder what the ideal way to do this would be. If we had some way of telling capture_io to use the pid of the GenServer, then it could properly set the group leader.
if you can’t wait for elixir 1.17, you can manually set the group_leader of the pid you want using :erlang.group_leader/2 to your test process and assert_receive on the messages that flow in.
Be careful! The order of the parameters on :erlang.group_leader/2 might not be the order you expect.