檢視來源 測試頻道
需求:本指南預設您已經完成了 測試入門指南。
需求:本指南預設您已經完成了 頻道指南。
在「頻道」指南中,我們看到一個「頻道」是一個具有不同組成的層次系統。據此,有時撰寫單元測試可能不足以應付我們的頻道功能。我們可能會想要驗證其不同的活動組成是否如我們預期般運作。此整合測試會確保我們正確地定義我們的頻道路由、頻道模組及其回呼;而較低層級的層級(如 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
類似 ConnCase
和 DataCase
,我們現在有一個 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_from
或 broadcast_from!
來做到這一點。兩者具有相同用途,唯一的區別是當廣播失敗時, broadcast_from!
會引發錯誤。
程式行 broadcast_from!(socket, "broadcast", %{"some" => "data"})
會觸發 handle_out/3
回呼,將相同的事件和負載推回用戶端。為了測試這一點,我們執行 assert_push "broadcast", %{"some" => "data"}
。
就這樣。現在您可以開發並完整測試即時應用程式。若要進一步了解測試頻道時提供的其他功能,請參閱 Phoenix.ChannelTest
的文件說明。