I want to test a function which is using Task.async
In order to make my test pass, I need to make it sleep for 100ms before assertions, otherwise the test process is killed before the async task is executed.
Is there a better way?
EDITED, adding code samples :
The code I would like to test (roughly) :
def search(params) do RateLimiter.rate_limit(fn -> parsed_params = ExTwitter.Parser.parse_request_params(params) json = ExTwitter.API.Base.request(:get, "1.1/search/tweets.json", parsed_params) Task.async(fn -> process_search_output(json) end) new_max_id(json) end) end
And the test I already wrote (working only with the call to sleep)
test "processes and store tweets" do with_mock ExTwitter.API.Base, [request: fn(_,_,_) -> json_fixture end] do with_mock TwitterRateLimiter, [rate_limit: fn(fun) -> fun.() end] do TSearch.search([q: "my query"]) :timer.sleep(100) # assertions assert called TStore.store("some tweet from my fixtures") assert called TStore.store("another one") end end end
Since the question is a bit vague, I will give the general answer here. The usual technique is to monitor the process and wait for the down message. Something like this:
task = Task.async(fn -> "foo" end) ref = Process.monitor(task.pid) assert_receive {:DOWN, ^ref, :process, _, :normal}, 500
Some important things:
The fifth element of the tuple is the exit reason. I am asserting the Task exit is :normal
. Change that accordingly if you are expecting another exit.
The second value in assert_receive
is the timeout. 500 miliseconds sounds like a reasonable amount given you currently have a 100 ms sleep.
When I cannot use José's approach involving assert_receive
, I use a small helper to repeatedly do assertion / sleep, until the assertion pass or finally times out.
Here is the helper module
defmodule TimeHelper do def wait_until(fun), do: wait_until(500, fun) def wait_until(0, fun), do: fun.() def wait_until(timeout, fun) defo try do fun.() rescue ExUnit.AssertionError -> :timer.sleep(10) wait_until(max(0, timeout - 10), fun) end end end
It can be used like this in former example:
TSearch.search([q: "my query"]) wait_until fn -> assert called TStore.store("some tweet from my fixtures") assert called TStore.store("another one") end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With