def async_query(query_def) do
spawn(fn -> IO.puts(run_query(query_def)) end)
end
def run_query(query_def) do
Process.sleep(50)
"#{query_def} result"
end
Normally, we could use ExUnit.CaptureIO.capture_io – but this is an async/spawn.
How do we unit test functions like this? (or show should they be written so they can be unit tested.)
captureIO should still work, the problem is that lambda in captureIO needs to wait, at least 50ms.
You should also almost never be using spawn, use Task functions instead, and if this is truly an async process, you should use Task.async. If you do that, then your captureIO can wrap the Task.await; and you don’t have to rely on time coordination between the code and your test.
The way that book is structured is supposed to build up on top of primitives. You’re not really supposed to be able to unit test stuff that uses spawn by itself, because spawn is a primitive. Testing will have you run up against limitations of concurrent systems, which you have discovered. Task module is created by Jose in part, to build on on top of the spawn primitives and make testing sane by overcoming these limitations of concurrency.
So, if you are trying to unit test the spawn example from sasa’s book as is, the “way to make it work” Is to rebuild the niceties of Task, which could be a good exercise in and of itself.
Thanks, this makes a lot of sense. What I ended up doing (which unfortunately breaks my own condition for “test code as is”) is to change the IO.puts into a send, and then to do an assert_received in the unit test.