ExUnit testing for receipt of multiple messages

548 views
Skip to first unread message

Richard Lack

unread,
Apr 25, 2015, 4:03:43 PM4/25/15
to elixir-l...@googlegroups.com
In some unit tests I'm writing, I need to test that multiple messages are being received.  The messages are coming from multiple processes, so the order they arrive varies when run the tests. Here is what I did to test it:

def assert_receive_all([]), do: :ok
def assert_receive_all(messages) do
receive do
msg ->
assert Enum.member?(messages, msg) # The message we received should be one of the expected ones.
messages = List.delete(messages, msg)
assert_receive_all(messages)
after
100 ->
raise ExUnit.AssertionError,
expr: messages,
message: "Did not receive all expected messages before timeout."
:timeout
end
end

And here's an example of how it's used:

# Verifies that these two messages are received, in any order, and nothing else.  
assert_receive_all([
{:child, "Zebra"},
{:infomesh, {:ecommons, :ooc}, "Cats"}])
 
Is there a better or pre-existing way to test for this sort of thing? If not, should we consider putting something like this into ExUnit?

Richard Lack

unread,
Apr 25, 2015, 4:10:13 PM4/25/15
to elixir-l...@googlegroups.com
I accidentally posted an old version. The one above wasn't detecting a case where I received an additional message after all the good messages arrived.  Here's a better version:

  def assert_receive_all([]) do
    receive do
      msg ->
        raise ExUnit.AssertionError,
        expr: msg,
        message: "Received an unexpected message"
      after
        10 -> :ok
    end
  end
  def assert_receive_all(messages) do
    receive do
      msg ->
        if !Enum.member?(messages, msg) do
          raise ExUnit.AssertionError,
            expr: msg,
            message: "Received an unexpected message"
        end
        assert Enum.member?(messages, msg)  # The message we received should be one of the expected ones.
        messages = List.delete(messages, msg)
        assert_receive_all(messages)
      after
        10 ->

Eric Meadows-Jönsson

unread,
Apr 25, 2015, 4:11:55 PM4/25/15
to elixir-l...@googlegroups.com

Something like this should work:

defmacro assert_receive_all(messages) do
  Enum.map(messages, fn msg ->
    quote do: assert_receive(unquote(msg))
  end)
end

--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/f4f11b3c-4869-4ebd-810d-35ec5a505e29%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Eric Meadows-Jönsson

Richard Lack

unread,
Apr 25, 2015, 4:22:17 PM4/25/15
to elixir-l...@googlegroups.com
Wow, Eric, you're right.  Based on what you wrote I realized assert_receive can match things in any sequence.  So all that code I wrote above can be deleted, and the test case can be reduced to:

assert_receive {:child, "Zebra"}
assert_receive {:infomesh, {:ecommons, :ooc}, "Cats"}
refute_receive _
Reply all
Reply to author
Forward
0 new messages