How to test result of asynchronous Task

I’m testing some logic that invokes a function which looks like this:

 defp publish_change(flag_name) do
    if Config.should_emit_events? do
      Task.start fn() ->
        Redix.command(@conn, ["PUBLISH" | Notifications.payload_for(flag_name)])
      end
    end
  end

I’m working with ExUnit on Elixir 1.4.1. I’m using the mock library for mocks and call verifications. Yes, I’ve read the articles on why mocks are discouraged.

My problem is that the moment Task.start() executes is not always immediate and my tests are not deterministic.
For example, I have a unit test where I assert that Redix.command is invoked with the right arguments. The test passes if I test that file only, but fails if I run the entire test suite, and I think it’s because the BEAM schedulers are busy and the Task is deferred.

I can make the test pass consistently with a simple :timer.sleep(10) between the assertions, but I’m wondering if there is a better or more idiomatic way to deal with this.

Thanks!

2 Likes

My suggestion would to be to establish a connection to Redis and assure you receive the published message after a timeout. An asynchronous system uses events to communicate, so the proper way to test async tasks is usually on receiving those events.

5 Likes

That makes sense, thanks.
I’m doing something similar in another integration test, where I exercise that the moving parts work together.

1 Like

@josevalim - trying to get MUCH better at testing my Elixir apps. Your statement - “An asynchronous system uses events to communicate, so the proper way to test async tasks is usually on receiving those events” - is wonderfully concise about how to test. (Dovetails very well with your mock post as well, since you have to inject the mock in order to redirect the messages to your test. ). Thank you.

1 Like