檢視來源 測試頻道

需求:本指南預設您已經完成了 入門指南 並運行一個鳳凰應用程式 已建立並正在執行

需求:本指南預設您已經完成了 測試入門指南

需求:本指南預設您已經完成了 頻道指南

在「頻道」指南中,我們看到一個「頻道」是一個具有不同組成的層次系統。據此,有時撰寫單元測試可能不足以應付我們的頻道功能。我們可能會想要驗證其不同的活動組成是否如我們預期般運作。此整合測試會確保我們正確地定義我們的頻道路由、頻道模組及其回呼;而較低層級的層級(如 PubSub 和 Transport)則會正確組態並依預期運作。

產生頻道

在我們持續學習本指南的過程中,具有一個具體的範例可供參考會有所幫助。鳳凰附帶一個 Mix 任務,可用來產生一個基本的頻道和測試。這些產生的檔案可用作撰寫頻道及其對應測試的良好範例。讓我們繼續產生我們的頻道

$ mix phx.gen.channel Room
* creating lib/hello_web/channels/room_channel.ex
* creating test/hello_web/channels/room_channel_test.exs
* creating test/support/channel_case.ex

The default socket handler - HelloWeb.UserSocket - was not found.

Do you want to create it? [Yn]  
* creating lib/hello_web/channels/user_socket.ex
* creating assets/js/user_socket.js

Add the socket handler to your `lib/hello_web/endpoint.ex`, for example:

    socket "/socket", HelloWeb.UserSocket,
      websocket: true,
      longpoll: false

For the front-end integration, you need to import the `user_socket.js`
in your `assets/js/app.js` file:

    import "./user_socket.js"

這將產生一個頻道、其測試,並指示我們在 lib/hello_web/channels/user_socket.ex 中新增一個頻道路由。加入頻道路由很重要,否則我們的頻道將無法運作!

ChannelCase

開啟 test/hello_web/channels/room_channel_test.exs,您會看到以下內容

defmodule HelloWeb.RoomChannelTest do
  use HelloWeb.ChannelCase

類似 ConnCaseDataCase,我們現在有一個 ChannelCase。當我們啟動我們的鳳凰應用程式時,所有這三個都會為我們產生。讓我們看看它。開啟 test/support/channel_case.ex

defmodule HelloWeb.ChannelCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with channels
      import Phoenix.ChannelTest
      import HelloWeb.ChannelCase

      # The default endpoint for testing
      @endpoint HelloWeb.Endpoint
    end
  end

  setup _tags do
    Hello.DataCase.setup_sandbox(tags)
    :ok
  end
end

它非常直觀。它會設定一個範例範本,匯入所有 Phoenix.ChannelTest 在使用時。在 setup 區塊中,它會啟動 SQL Sandbox,我們在 測試內容指南 中討論過它。

訂閱與加入

現在我們知道 Phoenix 提供了專門針對通道的客製測試案例及其提供什麼了,我們可以繼續了解 test/hello_web/channels/room_channel_test.exs 的其他部分。

首先,是設定區塊

setup do
  {:ok, _, socket} =
    HelloWeb.UserSocket
    |> socket("user_id", %{some: :assign})
    |> subscribe_and_join(HelloWeb.RoomChannel, "room:lobby")

  %{socket: socket}
end

setup 區塊會根據 UserSocket 模組建立 Phoenix.Socket,你可以在 lib/hello_web/channels/user_socket.ex 中找到。接著表示說我們想要訂閱並加入 RoomChannel,在 UserSocket 中可存取為 "room:lobby"。在測試的最後,我們回傳 %{socket: socket} 做為中繼資料,這樣就可以在每個測試中重複使用它。

簡而言之,subscribe_and_join/3 模擬用戶端加入頻道,並將測試程序訂閱到指定的議題。這是必要的步驟,因為用戶端在頻道上傳送和接收事件之前,必須先加入該頻道。

測試同步回應

我們產生的頻道測試的第一個測試區塊看起來像這樣

test "ping replies with status ok", %{socket: socket} do
  ref = push(socket, "ping", %{"hello" => "there"})
  assert_reply ref, :ok, %{"hello" => "there"}
end

這會測試 HelloWeb.RoomChannel 中的下列程式碼

# Channels can be used in a request/response fashion
# by sending replies to requests from the client
def handle_in("ping", payload, socket) do
  {:reply, {:ok, payload}, socket}
end

正如上面的註解所述,我們看到 reply 是同步的,因為它模擬了我們在 HTTP 中認識的請求/回應模式。當我們在伺服器處理完訊息後才想要寄送事件回傳給用戶端時,最好使用這種同步回應。例如,當我們將某個東西儲存到資料庫,然後只在儲存完後才寄送訊息給用戶端時。

test "ping replies with status ok", %{socket: socket} do 這行中,我們可以看到有一個地圖 %{socket: socket}。這讓我們可以使用設定區塊中的 socket

我們使用 push/3 來模擬用戶端推播訊息到頻道。在 ref = push(socket, "ping", %{"hello" => "there"}) 這行中,我們對頻道推播事件 "ping",其酬載為 %{"hello" => "there"}。這會觸發我們頻道針對 "ping" 事件的 handle_in/3 回呼。請注意,我們儲存了 ref,因為我們需要它在下行對回應進行斷言。使用 assert_reply ref, :ok, %{"hello" => "there"},我們斷言伺服器會傳送同步回應 :ok, %{"hello" => "there"}。這就是我們檢查是否觸發針對 "ping" 事件呼叫 handle_in/3 回呼的方式。

測試廣播

從用戶端接收訊息並對訂閱目前主題的所有人廣播是很常見。在 Phoenix 中,這種常見模式很容易表達,並且是 HelloWeb.RoomChannel 中產生的 handle_in/3 回呼之一。

def handle_in("shout", payload, socket) do
  broadcast(socket, "shout", payload)
  {:noreply, socket}
end

其對應的測試看起來像

test "shout broadcasts to room:lobby", %{socket: socket} do
  push(socket, "shout", %{"hello" => "all"})
  assert_broadcast "shout", %{"hello" => "all"}
end

我們注意到我們存取相同的 socket,這來自設定區塊。多方便啊!我們也執行與同步回覆測試中相同的 push/3。因此,我們 push 含有負載 %{"hello" => "all"}"shout" 事件。

由於 "shout" 事件的 handle_in/3 回呼只會廣播相同的事件和負載,因此 "room:lobby" 中的所有訂閱者都應該會收到訊息。為了檢查這一點,我們執行 assert_broadcast "shout", %{"hello" => "all"}

注意: assert_broadcast/3 會測試訊息在 PubSub 系統中已廣播。若要測試用戶端是否收到訊息,請使用 assert_push/3

測試伺服器發出的非同步推送

HelloWeb.RoomChannelTest 中最後的測試驗證了伺服器發出的廣播會推送到用戶端。與先前討論的測試不同,我們會間接測試頻道 handle_out/3 回呼是否被觸發。預設情況下, handle_out/3 已為我們實作,且僅將訊息推送到用戶端。

由於 handle_out/3 事件僅在我們從頻道呼叫 broadcast/3 時觸發,因此我們需要在我們的測試中模擬該動作。我們透過呼叫 broadcast_frombroadcast_from! 來做到這一點。兩者具有相同用途,唯一的區別是當廣播失敗時, broadcast_from! 會引發錯誤。

程式行 broadcast_from!(socket, "broadcast", %{"some" => "data"}) 會觸發 handle_out/3 回呼,將相同的事件和負載推回用戶端。為了測試這一點,我們執行 assert_push "broadcast", %{"some" => "data"}

就這樣。現在您可以開發並完整測試即時應用程式。若要進一步了解測試頻道時提供的其他功能,請參閱 Phoenix.ChannelTest 的文件說明。