Search code examples
elixir

Testing asynchronous code in Elixir


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

Solution

  • 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.