2017-01-09 10 views
3

メッセージを特定の順序で送信するエリクシールモジュールを構築しています。そして、私は問題がある。このエリクサーメッセージをアサートする方法

MyModule.send_msgs(self()) 
assert_received {:event, 1} 
assert_received {:event, 2} 
assert_received {:event, 3} 

のようなユニットテストを書いて、私はassert_received順序をシャッフルしても、テストはまだ合格します。

MyModule.send_msgs(self()) 
assert_received {:event, 2} 
assert_received {:event, 1} 
assert_received {:event, 3} 

assert_receivedは、メッセージ到着の順序を気にしないようです。 assert_receivedだけでなく、私が思い出す限り、receiveもメッセージが乱れることがあります。 ここで私の質問は、到着したメッセージが順序どおりに受信できない場合、その順序をどうやって保証できるのでしょうか?

+2

あなたはおそらく最初に順番に到着したメッセージに頼るべきではありません 場所。たとえば、そこに状態を構築するときに、受信者のメッセージを並べ替えることができます。必要に応じて、メッセージと一緒に注文指数を送信してください。 –

+1

@PatrickOscityはあまりにも非効率かもしれません。多くのモジュール、たとえば'gen_tcp'は、インデックスのないメッセージでデータを送信し、プロセスAからBに送信されたメッセージが確実に到着することが保証されているという事実に依存します。 – Dogbert

+0

@Dogbertわかりました。どういうわけか、もっと一般的なシナリオが考えられました。これは、最近取り組んでいるコードのために、複数のプロセスが「コレクタ」プロセスにメッセージを送信していたためです。もちろんその場合、着信メッセージの順序付けに対する保証はできません。 –

答えて

6

あり、これを行うために、既存の機能のように見えるが、メールボックスに次のメッセージが特定のパターンと一致することを主張する独自のマクロ作るのは簡単ですしていません。

defmacro assert_next_receive(pattern, timeout \\ 100) do 
    quote do 
    receive do 
     message -> 
     assert unquote(pattern) = message 
    after unquote(timeout) -> 
     raise "timeout" # you might want to raise a better message here 
    end 
    end 
end 

テスト:

defmodule MTest do 
    use ExUnit.Case 

    def send_msgs(pid) do 
    send pid, {:event, 1} 
    send pid, {:event, 2} 
    send pid, {:event, 3} 
    end 

    defmacro assert_next_receive(pattern, timeout \\ 100) do 
    quote do 
     receive do 
     message -> 
      assert unquote(pattern) = message 
     after unquote(timeout) -> 
     raise "timeout" # you might want to raise a better message here 
     end 
    end 
    end 

    test "should pass" do 
    send_msgs(self()) 
    assert_next_receive {:event, 1} 
    assert_next_receive {:event, 2} 
    assert_next_receive {:event, 3} 
    end 

    test "should fail" do 
    send_msgs(self()) 
    assert_next_receive {:event, 1} 
    assert_next_receive {:event, 3} 
    assert_next_receive {:event, 2} 
    end 
end 

出力:

. 

    1) test should fail (MTest) 
    test/m_test.exs:29 
    match (=) failed 
    code: {:event, 3} = message 
    right: {:event, 2} 
    stacktrace: 
     test/m_test.exs:32: (test) 



Finished in 0.05 seconds 
2 tests, 1 failure 

Randomized with seed 351622 
+0

驚くばかり!どのように動作するのか、そして 'assert_received'と' assert_receive'との違いを理解しようとしています。だから、 'assert_receive'のために、彼らは受信箱の中の所望のパターンと正確に一致するようにしています。そしてそれを行うことで、受信箱の中の所望のものの順序が何であるかは関係ありません。しかし、それをやったやり方は、どんなメッセージにもマッチしてそれを主張することでした。それはどんなメッセージでも、常にFIFO順にmsgを取得します。私はそれを正しく理解していますか? –

+0

はい、 'assert_received'と' assert_receive'はパターンと一致する最初のメッセージを選択し、 'assert_next_receive'はメールボックスの最初のメッセージがパターンと一致することを表明します。 – Dogbert

関連する問題