[elixir!#0019][訳]Phoenix WebSockets by Alex Jensenのテスト


テキスト
最近、Phoenixのwebsocketsを使ってゲームホールを作成する方法について発表しました.私たちのチームはテストを非常に重視しているので、今日は私たちが前に書いたwebsocketコードをテストする方法を紹介します.

準備作業


開始する前に、mix testを実行し、デフォルトのテストを削除し、テスト中の小さな問題を解決する必要があります.テストを作成する前に、他のエラーメッセージがないことを望んでいます.次に、Phoenix generatorが自動的に生成するchannelテストをすべて削除します.最初から作成するからです.

Helpers


Phoenixはすでにあなたのプロジェクトにいくつかのサポートファイルを作成して、あなたは中でよく使うテスト関数を定義することができて、他の言語のtest_helper/spec_helperに似ています.ここにはchannel test用のファイルtest/support/channel_case.exがあります.後でコードを追加します.quote do ... end構造に追加したコードは、各channel testモジュールによって参照されます.ここではMyAppのためにUserは別名を設定します.
using do
  quote do
    ...
    alias MyApp.{Repo, User}
    ...
  end
end

Lobby Channel Testの作成


テストの作成を開始します.まず、新しいファイルtest/channels/lobby_channel_test.exsを作成します.
defmodule MyApp.LobbyChannelTest do
  use MyApp.ChannelCase
  alias MyApp.LobbyChannel

  test "give me a dot" do
    assert 1 == 1
  end
end
use MyApp.ChannelCaseファイルからhelpersを導入するにはtest/supportが必要です.mix testを実行します.すべてが正しい場合、テストに合格したというメッセージが表示されます.

Socket試験杭


テストでユーザーのsocket接続をシミュレートするのは、予想以上に簡単かもしれません.私たちのコードでは、web/channels/user_socket.exファイルでチェックするtokenが必要です.チャンネルに接続されたsocketを直接作成できるので、検証する必要はありません.しかしながら、検証により得るcurrent_userの付与値を設定する必要がある.current_userのsocketを作成するには、次の方法を使用します.
def create_user(username) do
  %User{}
  |> User.changeset(%{username: username, password: "passw0rd", password_confirmation: "passw0rd"})
  |> Repo.insert
end

test "user receives the current list of users online" do
  {:ok, user} = create_user("jerry")
  {:ok, _, socket} =
    socket("", %{current_user: user})
    |> subscirbe_and_join(LobbyChannel, "game:lobby")
end

ここでは、所定のユーザ名を用いて新しいユーザを作成する関数を定義する.この関数はtest/support/channel_case.exファイルにも定義できるが、ここに残しておく.ユーザを作成した後、Phoenixが導入した関数であるsocket関数に渡す.これでこのソケットのassignsが設定されました.その後、socketとchannelモジュール、および加入する部屋名をsubscribe_and_join関数に一括して入力.成功すると{:ok, response, socket}に戻りますが、ロビーシステムから値を返すことはありませんので、_を使用して無視します.

Assertions


ユーザーを作成してchannelに参加しましたが、現在のロビーの状態を受け取ったかどうかをどのように判断しますか?私たちが参加したchannelは、現在のユーザーを含む応答を返すのではなく、joinイベントの後にデータをブロードキャストします.Phoenixは、放送を確認するための関数assert_broadcastを提供する.
チャンネルテストを実行すると、Phoenixはイベント名とデータを含む送信ごとのブロードキャストのリストを保存する.assert_broadcastは、このリストで所望のブロードキャストを検索し、見つかったらtrueを返します.このassertionは、タイムアウト(assert_broadcast関数の3番目のパラメータ)前に所望のブロードキャストが送信されなければ失敗する.ここで、assertが放送するイベントはlobby_updateで、["jerry"]を含むusersが付属しています.
test "user receives the current list of users online" do
  {:ok, user} = create_user("jerry")
  {:ok, _, socket} =
    socket("", %{current_user: user})
    |> subscribe_and_join(LobbyChannel, "game:lobby")
   
  assert_broadcast "lobby_update", %{users: ["jerry"]}
end

クリーンアップ


このlobby_updateのテストは今合格できるはずですが、もう一つの小さな問題があります.ユーザが加入すると現在のユーザリストに追加され、離れると削除されますが、私たちは現在加入しているだけです.Phoenixが関数から離れることに感謝します.私たちはこのように使用することができます.
test "user receives the current list of users online" do
  {:ok, user} = create_user("jerry")
  {:ok, _, socket} =
    socket("", %{current_user: user})
    |> subscribe_and_join(LobbyChannel, "game:lobby")
    
  assert_broadcast "lobby_update", %{users: ["jerry"]}
  leave socket
end

もう一人のHelper


続行する前に、別のhelper関数を作成してユーザーを作成し、「game:lobby」チャンネルに参加しましょう.後のテストでこの手順を繰り返す必要があるからです.
def create_user_and_join_lobby(username) do
  {:ok, user} = create_user(username)

  socket("", %{current_user: user})
  |> subscirbe_and_join(LobbyChannel, "game:lobby")
end

以前のテストを次のように変更できます.
test "user receives the current list of users online" do
  {:ok, _, socket} =
    create_user_and_join_lobby("jerry")
  assert_broadcast "lobby_update", %{users: ["jerry"]}
  leave socket
end

ゲーム招待のテスト


ゲームの招待をテストするために、2人のユーザーを作成し、招待イベントをサーバに送信し、招待がユーザーに送信されているかどうかを確認します.
test "user receives an invite" do
  {:ok, _, socket1} =
    create_user_and_join_lobby("bill")
  {:ok, _, socket2} =
    create_user_and_join_lobby("will")
  
  push socket1, "game_invite", %{"username" => "will"}
end

「game_invite」のコードを見に行くと、送信者とユーザー名を含むブロードキャストが発行されていますが、ブロードキャストがブロックされ、正しいユーザーにしか送信されません.ここでは、チェックしたいメッセージが放送されていないため、assert_broadcastを使用することはできません.assert_pushを使用できます.
test "user receives an invite" do
  {:ok, _, socket1} =
    create_user_and_join_lobby("bill")
  {:ok, _, socket2} =
    create_user_and_join_lobby("will")
  
  push socket1, "game_invite", %{"username" => "will"}
  assert_push "game_invite", %{username: "bill"}
end

以上のコードを観察すると、usernameキーが文字列であるか、原子であるかを知りたいかもしれません.通常、あなたのsockeはすべてのものをJSONに符号化してクライアントに送信します.これはすべて文字列になります.channelテストはchannelに対してより直接的な制御を持っているので、実際に受信したデータと完全に一致することを確保しなければならない.
すべてのソケットを離れたことを確認してください.
test "user receives an invite" do
  {:ok, _, socket1} =
    create_user_and_join_lobby("bill")
  {:ok, _, socket2} =
    create_user_and_join_lobby("will")
  
  push socket1, "game_invite", %{"username" => "will"}
  assert_push "game_invite", %{username: "bill"}
  leave socket1
  leave socket2
end

まとめ


Phoenixのwebsocketsをテストする方法を簡単に紹介した.最初はテストが怖いと思っていたかもしれませんが、実際には簡単で速いです.大規模なアプリケーションでは、強力なhelperを定義することが特に重要である.ExUnitのドキュメントのさまざまな機能を確認してください.