[elixir! #0019][譯] 測試Phoenix WebSockets by Alex Jensen

原文html

最近我發表了一篇關於如何使用Phoenix的websockets建立一個遊戲大廳的文章. 咱們團隊很是重視測試, 因此, 今天我將介紹如何測試咱們以前編寫的websocket代碼.web

準備工做

在開始以前, 咱們須要運行 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 設置一個別名:websocket

using do
  quote do
    ...
    alias MyApp.{Repo, User}
    ...
  end
end

建立 Lobby Channel Test

如今開始編寫測試. 首先建立一個新文件 test/channels/lobby_channel_test.exssocket

defmodule MyApp.LobbyChannelTest do
  use MyApp.ChannelCase
  alias MyApp.LobbyChannel

  test "give me a dot" do
    assert 1 == 1
  end
end

注意, 咱們須要 use MyApp.ChannelCase 來從 test/support 文件中引入 helpers.函數

運行 mix test, 若是一切正確你會收到測試經過的消息.測試

Socket 測試樁

在測試中模擬用戶的socket鏈接, 可能比你預想的要簡單. 在咱們的代碼裏, 用戶須要一個token才能鏈接, 它是在 web/channels/user_socket.ex 文件中進行檢查的. 由於咱們能夠直接建立一個鏈接到channel的socket, 因此咱們不須要經過任何驗證. 然而, 咱們仍然須要設置一個由驗證得到的 current_user 賦值.ui

使用以下方法建立一個帶有 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 文件中, 但如今先把它留在這裏. 建立用戶以後, 咱們將其傳遞給 socket 函數, 它是Phoenix引入的函數. 這樣就設置好了這個socket的 assigns. 而後將socket 和 channel模塊, 以及要加入的房間名一併傳入 subscribe_and_join 函數. 若是成功會返回 {:ok, response, socket}, 不過咱們沒有從大廳系統返回任何值, 因此使用 _ 來忽略.code

Assertions

咱們已經建立了一個用戶並加入了 channel, 但如何判斷它是否收到了當前的大廳狀態呢? 咱們加入的 channel 不會返回一個包含當前用戶的迴應, 而是在 join 事件以後將數據廣播出來. Phoenix 提供了一個用於檢查廣播的函數 assert_broadcast .

當運行一個 channel test 時, Phoenix會保存一個每次發送的廣播的列表, 包含事件名和數據. assert_broadcast 會在這個列表中查找你所指望的廣播, 若是找到則返回 true. 若是你指望的廣播沒有在超時(assert_broadcast函數的第三個參數)以前被髮送, 這個assertion 便會失敗. 在這裏, 咱們 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

測試遊戲邀請

爲了測試咱們的遊戲邀請, 咱們將建立兩個用戶, 發送邀請事件到服務器, 並檢查是否有邀請發送給用戶:

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更直接的控制, 因此咱們要確保和實際收送的數據徹底一致.

確保你離開了每一個socket:

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 的文檔中的各類功能.

相關文章
相關標籤/搜索