4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ActionCableのRspecでメッセージの送受信をテストする

Posted at

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は最近できた機能なので情報が少なく、検索も一苦労でした。頑張りました。。。。。

参考

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?