ActionCableのRspecでメッセージの送信から配信までをテスト
ActionCableは最近できた機能なのであまり情報がなく、Rspecの実装に非常に時間がかかったのでアウトプット。
同じようにお困りの方がいれば参考にしてほしいです。
今回は以下のメソッドによるメッセージの送受信をテストします。
class MessageChannel < ApplicationCable::Channel
# メッセージを保存し、ブロードキャストするためのアクション
def post(data)
@chat_group = ChatGroup.find(params['chat_group_id'])
@message = @chat_group.messages.build(text: data['message'])
if @message.save
ActionCable.server.broadcast "message_channel_#{params['chat_group_id']}", message: @message, group: @chat_group
end
end
このメソッドのテストで重要なのは以下の2つ。
- textカラムの値を送るとメッセージが保存されること
- 接続しているチャネルにメッセージが配信されること
これらがテストできればOKとしました。
実際のテストはこうなりました。
require 'rails_helper'
RSpec.describe MessageChannel, type: :channel do
before do
@chat_group = create(:chat_group)
stub_connection
end
describe "メッセージの送信" do
before do
@message = build(:message, chat_group_id: @chat_group.id) #postメソッド内でメッセージを保存するのでここではbuildするだけ
end
context "送信成功" do
it "グループが選択されている状態でメッセージを送信するとメッセージが一つDBに保存される" do
subscribe(chat_group_id: @chat_group.id)
expect(subscription).to be_confirmed
expect do
perform :post, message: @message.text
end. to change(Message, :count).by(1)
end
it "グループが選択されている状態でメッセージを送信するとメッセージが接続しているチャネルに配信される" do
subscribe(chat_group_id: @chat_group.id)
expect(subscription).to be_confirmed
expect do
perform :post, message: @message.text
end. to have_broadcasted_to("message_channel_#{@chat_group.id}").with{ |data|
expect(data['message']['text']).to eq @message.text
}
end
end
context "送信失敗" do
it "グループが選択されていない状態でメッセージを送信するとメッセージが保存できない" do
subscribe(chat_group_id: nil)
expect do
perform :post, message: @message.text
end. to raise_error RuntimeError #Must be subscribed! というエラーが出ることを検証
end
end
end
end
詳しくみていきます
beforeブロック
メソッド内で保存処理を行うのでbeforeブロックではインスタンスの生成を行った。
before do
@message = build(:message, chat_group_id: @chat_group.id) #postメソッド内でメッセージを保存するのでここではbuildするだけ
end
正常形
正常形は2つexampleを設定
context "送信成功" do
it "グループが選択されている状態でメッセージを送信するとメッセージが一つDBに保存される" do
subscribe(chat_group_id: @chat_group.id)
expect(subscription).to be_confirmed
expect do
perform :post, message: @message.text
end. to change(Message, :count).by(1)
end
it "グループが選択されている状態でメッセージを送信するとメッセージが接続しているチャネルに配信される" do
subscribe(chat_group_id: @chat_group.id)
expect(subscription).to be_confirmed
expect do
perform :post, message: @message.text
end. to have_broadcasted_to("message_channel_#{@chat_group.id}").with{ |data|
expect(data['message']['text']).to eq @message.text
}
end
end
1つめはデータの保存ができることを確認。2つめは接続しているチャネルに保存したメッセージが配信されることを確認。1つめはよく見るデータの保存処理。request_specに書き慣れている人なら余裕だと思う。
2つめはかなりややこしい。ここで8時間かかった。
expect do
perform :post, message: @message.text
end. to have_broadcasted_to("message_channel_#{@chat_group.id}").with{ |data|
expect(data['message']['text']).to eq @message.text
expect(data['message']['id']).not_to eq nil #idが存在していることを確認
}
この部分はperformでpostメソッドを動かしたときにbroadcastされるdataの内容をブロック変数dataで受け取っている。ブロック内でdataのidがnilではなく、かつtextが送ったデータと一致することを検証。
こうすることで送ったデータが保存できていることを検証できる。
ちなみにdataの中身をbinding.pryで確認すると以下のようになる。
51: expect do
52: perform :post, message: @message.text
53: end. to have_broadcasted_to("message_channel_#{@chat_group.id}").with{ |data|
54: expect(data['message']['text']).to eq @message.text
55:
=> 56: binding.pry
57:
58: }
59: end
60: end
61:
[1] pry(#<RSpec::ExampleGroups::MessageChannel::Nested::Nested>)> data
=> {"message"=>{"id"=>161, "text"=>"test message", "chat_group_id"=>618, "created_at"=>"2021-02-18T23:53:20.494Z", "updated_at"=>"2021-02-18T23:53:20.494Z"},
これは実行環境でbroadcastされるdataと一致している。
[ActionCable] Broadcasting to message_channel_35: {:message=>#<Message id: 8, text: "test", chat_group_id: 35, created_at: "2021-02-18 23:57:33", updated_at: "2021-02-18 23:57:33">}
異常形
subscribeされていない状態でメッセージを送ろうとするとエラーが発生することを検証。
これもrequest_specとか書き慣れている人であればあまり苦労しない
context "送信失敗" do
it "グループが選択されていない状態でメッセージを送信するとメッセージが保存できない" do
subscribe(chat_group_id: nil)
expect do
perform :post, message: @message.text
end. to raise_error RuntimeError #Must be subscribed! というエラーが出ることを検証
end
end
これにてpostメソッドのテスト実装完了。一つのexampleに8時間とかかけたのはほんまに大変で泣きそうでした。
そもそもなんのメソッドが使えるのかがわからないので必死にbinding.pryで調べたりしてた。ActionCableは最近できた機能なので情報が少なく、検索も一苦労でした。頑張りました。。。。。