15

I am wondering how to test ActionCable channels.

Let's say I have the following chat channel:

class ChatChannel < ApplicationCable::Channel
  def subscribed
    current_user.increment!(:num_of_chats)

    stream_from "chat_#{params[:chat_id]}"
    stream_from "chat_stats_#{params[:chat_id]}"
  end
end

The subscribed method updates the db and defines two streams to be broadcasted across the channel, but the details are not very important since my question is a more general one:

  • How can I set up a test to test the logic involved by subscribing to this channel?

RSpec provides a lot of helper methods and various utilities when testing similar interactions like controller actions, but I couldn't find anything regarding RSpec and ActionCable.

Sajad Torkamani
  • 544
  • 1
  • 7
  • 18
Dani
  • 1,012
  • 9
  • 15

5 Answers5

8

You probably want to wait* for https://github.com/rails/rails/pull/23211 to be merged. It adds ActionCable::TestCase. Once that's merged, expect the rspec-rails team to do its part: https://github.com/rspec/rspec-rails/issues/1606

* Waiting is optional; You can not wait and based your own work on this "work in progress" and find a solution that works right now.

Leonel Galán
  • 6,993
  • 2
  • 41
  • 60
6

You can use `action-cable-testing` gem.

Add this to your Gemfile
gem 'action-cable-testing'
Then run
$ bundle install

Then add following spec

# spec/channels/chat_channel_spec.rb

require "rails_helper"

RSpec.describe ChatChannel, type: :channel do
  before do
    # initialize connection with identifiers
    stub_connection current_user: current_user
  end

  it "rejects when no room id" do
    subscribe
    expect(subscription).to be_rejected
  end

  it "subscribes to a stream when room id is provided" do
    subscribe(chat_id: 42)

    expect(subscription).to be_confirmed
    expect(streams).to include("chat_42")
    expect(streams).to include("chat_stats_42")
  end
end

For more information see readme in github repo.

https://github.com/palkan/action-cable-testing

There are examples for both rspec and test_case

zhisme
  • 2,368
  • 2
  • 19
  • 28
3

I would install and configure TCR gem for recording sockets interaction ('its like VCR for websockets')

A spec for this in your case might look something like this...

describe ChatChannel do
  context ".subscribed" do
    it "updates db and defines opens 2 streams for one channel" do
      TCR.use_cassette("subscribed_action") do |cassette|
        # ...
        ChatChannel.subscribed
        expect(cassette).to include "something ..."
      end
    end
  end
end
MilesStanfield
  • 4,571
  • 1
  • 21
  • 32
  • 2
    That's a very good idea, thanks, but (and correct me if I'm wrong) using VCR (and TCR analogously) makes sense when you want to stub external http requests (for example if my controller action issues an external call), but this is not the case here - here I just want to invoke my own internal methods. But once again - it's a good idea and I'll give it a go. – Dani Feb 06 '16 at 10:24
  • 1
    Hey Dani, have you found a solution? If so, could you point out a repo that I could check out? – Sean Magyar Sep 30 '16 at 09:24
  • Hey @SzilardMagyar have you found any solution? – Tanay Sharma Mar 15 '18 at 06:36
2

Now Rails 6 includes the action-cable-test gem

So there is no need to add a gem. You can do either

assert_has_stream "chat_1"

or, with rspec:

expect(subscription).to have_stream_from("chat_1") 
Tabare
  • 151
  • 3
1

Look like it was merged to Rails 6. Checkout the release note Action cable testing and the pr AC testing PR