GithubHelp home page GithubHelp logo

palkan / action-cable-testing Goto Github PK

View Code? Open in Web Editor NEW
209.0 4.0 17.0 172 KB

Action Cable testing utils

License: MIT License

Ruby 85.86% HTML 0.37% Gherkin 13.77%
rails actioncable testing minitest rspec

action-cable-testing's Introduction

Hey / Привет 👋

My name is Vladimir (or Вова), and I'm a principal backend engineer at @evilmartians.

📕 My book "Layered design for Ruby on Rails applications" is avalable now: Amazon | Packt

I'm working on:

Check out some of my blog posts:

...and conference talks:

action-cable-testing's People

Contributors

depfu[bot] avatar jasonfb avatar johnnaegle avatar jsgarvin avatar millnitzluan avatar newyork-anthonyng avatar palkan avatar paulccarey avatar sponomarev avatar thesmartnik avatar yagihiro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

action-cable-testing's Issues

undefined local variable or method `streams'

I'm attempting to test the stream in ActionCable with RSpec. I have the stream working all the way up to the front end but would like a solid test for the streams. Below is an example of what I'm attempting to do:

require 'rails_helper'
RSpec.describe NotificationsChannel, type: :channel do
  it 'successfully subscribes and streams message' do
    user = create(:user)
    stub_connection current_user: user
    subscribe
    key = "my:redis:key"
    $redis.publish(key, 'true')
    expect(subscription).to be_confirmed
    expect(subscription).to have_stream_from key
    expect(streams).to include('true')
  end
end

receiving this error

NameError:
       undefined local variable or method `streams' for...

I have followed all the documentation of adding the action-cable-testing gem and its necessary require statement in rails_helper.rb

using Rails 6.0.2.1

Add ability to match multiple broadcasts

def make_multiple_broadcasts
  ActionCable.server.broadcast "user_channel", message: "Hi"
  ActionCable.server.broadcast "user_channel", message: "Bye"
end

The above function broadcasts multiple times. Is there a way to test this with action-cable-testing, without calling make_multiple_broadcasts twice?

How do I unsubscribe testing

Hello.
I'm using action-cable-testing.
I succeed subscribe testing. However, I have no idea how to test unsubscribe.
Thank you for telling me.

params don't seem to be parsed out of connection requests

If I mount my ActionCable app as such:

mount ActionCable.server => "/socket(/:channel/:resource_id)"

My connection objects receive and have access to request.params[:channel] and request.params[:resource_id] much as I would expect.

If I then write a test to verify the behavior of my Connection class' authentication logic

expect { connect "/socket/somechannel/somebogusid" }.to have_rejected_connection

My Connection class receives empty params in the Request object. This basically means I cannot test anything I need to.

Am I missing something or is this a legitimate deficiency of this library?

docs about compatibility/how-to-migrate

I'm so happy that the related codes are finally merged into rails. Thanks for the gem, it's a saver for years.

Currently, I'm using this gem in several projects, I'd like to know if I can migrate to rails and drop one more dependency.

Also, It would be great if someone could note the compability of this gem and anycable somewhere.

Thanks!

Trigger subscription callback when there is a publish action

When using GraphqlRuby, I'm using ActionCable to support Graphql#Subscription. In order to support that, I need to write a customized Channel for the updates.

Therefore, I'm trying to write test for my Channel implementation, using this library.

I'm writing code to test the general channel behaviour, that works fine; however, when I'm trying to test the flow of "server push data to client", I'm facing a problem with the incompatibility between how Graphql#Subscription is implemented by GraphqlRuby, and this library:

How Graphql#Subscription is implemented by GraphqlRuby

When there is a subscription request, sth like:

    subscription SubscribeUser($uuid: String) {
      user(uuid: $uuid) {
        name
        avatar {
          cloudinaryId
          cloudinaryType
        }
      }
    }

2 subscriptions would happen:

  1. it would subscribe for any updates, based on the query param: https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/subscriptions/action_cable_subscriptions.rb#L101; and furthermore, executes the query (i.e., trigger next step)
  2. it would subscribe for a given subscription_id, and whenever a broadcast happens (from first step), it would deliver this broadcast to the corresponding client
    https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/subscriptions/action_cable_subscriptions.rb#L97,

In short, this library is using ActionCable#pubsub in 2 ways:

  1. the normal way as we're expecting, push directly to client
  2. using PubSub as a way for passing messages internally

The problem I'm facing

It seems to me that action-cable-testing has overwrote the Connection & Channel: https://github.com/palkan/action-cable-testing/blob/master/lib/action_cable/channel/test_case.rb#L21, therefore making it unable to trigger the default PubSub callback (i.e., step 2 above).

So I want to ask, in such a case, what do u recommend for testing part of triggering callback internally?

Readme example issue

Hi!

I've just spotted that in this example from readme you're calling let(:post) and then calling post :create. I haven't tested this but I believe that let will overwrite post method so I think the example is incorrect.

RSpec.describe CommentsController do
  describe "POST #create" do
    let(:post) { create :post }

    expect { post :create, comment: { text: 'Cool!', post_id: post.id } }.to
      have_broadcasted_to(post).from_channel(PostChannel).with(text: 'Cool!')
  end
end

Rails 6: undefined method `stub_connection'

I was using this gem in Rails 5.x/RSpec before it was merged into Rails 6, and it worked great. I've got Rails 6 beta 1 running, and obviously I've commented out the gem now.

However, when I run my ActionCable tests, I'm getting NoMethodError: undefined method 'stub_connection'. It looks like I need to include something, and require "action_cable/testing/rspec" is no longer available. What am I missing here?

Thanks for the hard work, and congratulations on getting it merged into Rails.

have_broadcasted_to does not seem to work in Rails 5.2

Currently I am on RSpec 4 with Rails 5.2. In order to get action cable test to work, I used the monkey patch as referenced from #76.

Channel testing works fine, however I cant seem to get have_broadcasted_to to work successfully. I have checked the logs and its shows that ActionCable is broadcasting to its respective channel with its respective message but the test sees that the broadcast is still 0.

Any idea on this or is this gem simply not compatible with my current setup?

Testing "transmit"

Hi! I would like to test that when a method is invoked, the server sends back some data only to the current subscriber using trasnmit instead of broadcast. How would I go about testing this? Thanks!

Strange behaviour of expect(subscription).to be_rejected

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private

    def find_verified_user
      if (verified_user = env['warden'].user)
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end
# app/channels/conversation_channel.rb
class ConversationChannel < ApplicationCable::Channel
  def subscribed
    lead = Lead.find_by(id: params['lead_id'], user_id: identifier.id)
    reject if lead.blank?
    stream_from "conversation_#{params['lead_id']}_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def receive(data)
    lead = Lead.find_by!(id: params['lead_id'], user_id: current_user.id)
    Messenger::SendConversationMessage.send_message(text: data['text'], lead: lead)
  end
end
# spec/channels/conversation_channel_spec.rb
require 'rails_helper'

RSpec.describe ConversationChannel, type: :channel do
  let(:lead) { create(:lead) }
  let(:user) { lead.user }

  before do
    stub_connection lead_id: lead.id, identifier: user
  end

  it 'rejects when no lead id' do
    subscribe
    expect(subscription).to be_rejected
    expect(subscription).not_to have_streams
  end

  it 'subscribes to a stream when lead id is provided' do
    subscribe(lead_id: lead.id)
    expect(subscription).to be_confirmed
    expect(subscription).to have_stream_from("conversation_#{lead.id}_channel")
  end
end

RSPEC output

rspec ./spec/channels/conversation_channel_spec.rb

ConversationChannel
  rejects when no lead id (FAILED - 1)
  subscribes to a stream when lead id is provided

Failures:

  1) ConversationChannel rejects when no lead id
     Failure/Error: expect(subscription).not_to have_streams

     RuntimeError:
       Must be subscribed!
     # /Users/prakash/.rvm/gems/ruby-2.6.1/gems/action-cable-testing-0.5.0/lib/action_cable/testing/channel/test_case.rb:289:in `check_subscribed!'
     # ./spec/channels/conversation_channel_spec.rb:14:in `block (2 levels) in <top (required)>'
     # ./spec/rails_helper.rb:58:in `block (2 levels) in <top (required)>'

Finished in 0.59609 seconds (files took 2.91 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/channels/conversation_channel_spec.rb:11 # ConversationChannel rejects when no lead id

Failing subscription returns nil instead of true

Per the README:

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

This test fails with the following message:

Failure/Error: expect(subscription).to be_rejected
       expected `#<MyChannel:0x007fb69d350fb0 @connection=#<ActionCable::Channel::ConnectionStub:0x007fb69d351460 ...ue:0>, @reject_subscription=nil, @subscription_confirmation_sent=true, @_streams=["my_channel"]>.rejected?` to return true, got nil

Should we be testing for nil here instead of rejected??

NotImplementedError on disconnect

I'm getting the following error in my logs when my channel disconnects in a Feature spec.

[ActionCable] [user:960328000:tiffany] Finished "/cable/" [WebSocket] for 127.0.0.1 at 2018-12-17 11:44:19 -0700
[ActionCable] [user:960328000:tiffany] There was an exception - NotImplementedError(NotImplementedError)
[ActionCable] [user:960328000:tiffany] /home/jsgarvin/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actioncable-5.2.2/lib/action_cable/subscription_adapter/base.rb:22:in `unsubscribe'
`

Adding the following method to ActionCable::SubscriptionAdapter::Test in a local copy of the gem seems to correct the issue.

def unsubscribe(*args)
  #no-op
end

I'd be happy to submit a PR with the correction, but there don't seem to be any existing tests around ActionCable::SubscriptionAdapter::Test, so I'm not sure how I would go about including a test for it.

Does anyone has the same error?

I'm trying to test some broadcasting (in my case a it will be after a worker does some task) and I always give this error:
Failure/Error: expect { worker.perform(param1,param2) }.to ArgumentError: wrong number of arguments (given 0, expected 1..2)

It does not matter, whatever I write inside the expect I get the same error.

My set up includes:

  • 'rspec-rails', '4.0.1'
  • 'rails', '6.0.3.2'

And of course:

  • 'action-cable-testing'

Also I have required it in my rails_helper after the environment require:

require File.expand_path('../config/environment', __dir__)
require 'action_cable/testing/rspec'

Any ideas or suggestion?
Thanks to everyone in advance!

Errors with Anycable check

When running specs the AnyCable::Compatibility checks are raising an error for setting @_streams (Channel instance variables are not supported by AnyCable, but were set: @_streams).

The adapter is set to test as suggested in the readme, but the compatibility checks are loaded via an initializer, so they get applied (and should be as per the documentation). I am not sure if there is something missing/wrong in our setup, but i think this is a problem in either anycable (not knowing about testing instance variable(s)) or action-cable-testing (using a workaround for tracking streams that violate anycable expectations).

Both seem to be weak arguments, so i am not sure which one is the better path, or if there are other (better) alternatives.

Any ideas about using this gem with `perform_later` method?

Hello! In my project I want to use perform_later method to achieve higher performance, maintainability of my sidekiq part of application. But because of perform_later is async method, inside my assignment in Rspec, I have:

      it 'broadcasts any event for user that has access' do
        expect{ subject }.to have_broadcasted_to(stream_name(user: user, object_id: 1))
      end

But this code is called asynchronously. Inside my Rails application:

  def broadcast_event_to_users_channel(event:)
    Api::V1::BroadcastUserEventJob.perform_later(user: user)
  end

And inside of my job:

module Api
  module V1
    class BroadcastUserEventJob < ApplicationJob
      queue_as :default

      def perform(user:)
        Dispatchers::UsersDispatcher.broadcast(event.to_sym,  user: user)
      end
    end
  end
end

Any ideas about using it with async things?
@palkan

Matcher for Channel.broadcast_to

Hi 😃

Channels in ActionCable have .broadcast_to method (docs), which can be called both inside and outside of a channel. It also accepts object/record as a first argument. Right now there are a couple of ways to check that this method was called, but I think it'd be a good idea to add a matcher for it.


Possible workarounds

  • Stub
expect(WebNotificationsChannel).to receive (:broadcast_to)
  • Manual construction of a stream
stream = WebNotificationsChannel.broadcasting_for([WebNotificationsChannel.channel_name, session])
expect { perform :action }.to have_broadcasted_to(stream)

Suggestions

  • Rename current have_broadcasted_to matcher to have_broadcasted, and add above functionality to have_broadcasted_to.

I think that will be more straightforward. have_broadcasted corresponds to #broadcast and have_broadcasted_to corresponds to .broadcast_to. However one is an instance method, while the other one is a class method. Maybe matchers should emphasize the difference.

  • Add channel_broadcast_to matcher

This one clearly states the distinction between methods. However, I don't think that it is as intuitive as an option above.

In both ways, though, I don't really know what would be a good syntax for specifying channel name.

What do you think?

Test connection with Devise session

I try to test a connection with my devise authorization:

def find_verified_user
      if (verified_user = env['warden'].user)
        verified_user
      else
        reject_unauthorized_connection
      end
end

and fail still env doesn't contain warden key.

From the Readme:

RSpec.describe ApplicationCable::Connection, type: :channel do
  it "successfully connects" do
    connect "/cable", headers: { "X-USER-ID" => "325" }
    expect(connection.user_id).to eq "325"
  end
  ...
end

In my view, test connection creates own env, which is not shared with warden. How can I solve it?

Error in rspec test, subscribe action ...

I'm trying to test successful subscribe with channel ...
and I'm getting this error .. because of calling stream_from ...

     NoMethodError:
       undefined method `server' for #<ActionCable::Channel::ConnectionStub:0x007f84a96be678>
     # /Users/yaman/.rvm/gems/ruby-2.3.1/gems/actioncable-5.0.0.1/lib/action_cable/channel/streams.rb:85:in `stream_from'
.....

server method is missing from ConnectionStub, I just tried to add it like this:

class ConnectionStub
  attr_reader :transmissions, :identifiers, :subscriptions, :logger

  def initialize(identifiers = {})
    ...
  end

  def server
    ActionCable.server
  end

  ...
end

and it works! .. any idea ?!

my original code is like this:

class ConversationChannel < ApplicationCable::Channel
  def subscribe_to_channel
    stop_all_streams
    stream_from conversation_id
  end
end

action-cable-testing does not work with rspec-rails 4.0 and rails 5

The issue happens due to the following check in action_cable/testing/rspec.rb:
if RSpec::Rails::FeatureCheck.respond_to?(:has_action_cable_testing?).
It does not account for a fact that though the method is present it still returns false for Rails 5.

I suggest using if RSpec::Rails::FeatureCheck.respond_to?(:has_action_cable_testing?) && RSpec::Rails::FeatureCheck.has_action_cable_testing? instead.

multiple connections testing

Hey guys, we're trying to implement some multi-user multi-connection testings and have got no luck by far.

We could always stub_connection multiple times, but we haven't found any options to assign different connection to subscribe.

Any advice?

Stub connection is overwrites real connection class

I have following code:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
      logger.add_tags 'ActionCable', current_user.name
    end

    protected

    def find_verified_user
      verified_user = User.find_by(id: cookies.signed['user.id'])
      if verified_user && cookies.signed['user.expires_at'] > Time.zone.now
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

So for example when I run specs for my channel I expect to my test real connection in application.

But if I have something following in my channel:

class TodoChannel < ApplicationCable::Channel
  def subscribed
    stream_from "todo_channel_#{current_user.id}"
  end
end

I get following error:

Failures:

  1) TodoChannel subscribe 
     Failure/Error: stream_from "todo_channel_#{current_user.id}"
     
     NameError:
       undefined local variable or method `current_user' for #<TodoChannel:0x00007fcf8f17aa00

So it comes to that I can't test my connection in application, but instead of it I should write test to a class defined in gem. I think it is not the reason of testing it all.

Maybe we need to use something is like
pseudo-code

if defined?(described_class)
use described_class as stub
....

What do you think?

Rails 6.1 support (stop_stream_from)

Is this gem still being developed here or has it all moved to the Rails project? Where will Rails 6.1 support be added?

I ask because I ran into this error:

     Failure/Error: stop_stream_from uuid
     NoMethodError:
       undefined method `unsubscribe' for nil:NilClass

I was able to fix by putting the following at the top of my channel spec:

module ActionCable
  module Channel
    class ConnectionStub
      def pubsub
        ActionCable.server.pubsub
      end
    end
  end
end

RSpec uninitialized constant ActionView::Template::Handlers::ERB::ENCODING_FLAG

Following the advised RSpec approach, running the RSpec test fails with:

/home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template/handlers/erb.rb:90:in `<class:ERB>': uninitialized constant ActionView::Template::Handlers::ERB::ENCODING_FLAG (NameError)
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template/handlers/erb.rb:76:in `<module:Handlers>'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template/handlers/erb.rb:5:in `<class:Template>'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template/handlers/erb.rb:4:in `<module:ActionView>'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template/handlers/erb.rb:3:in `<top (required)>'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template/handlers.rb:12:in `extended'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template.rb:112:in `extend'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template.rb:112:in `<class:Template>'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template.rb:8:in `<module:ActionView>'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template.rb:6:in `<top (required)>'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/template/resolver.rb:4:in `<top (required)>'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/actionview-5.0.6/lib/action_view/testing/resolvers.rb:1:in `<top (required)>'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/rspec-rails-3.7.1/lib/rspec/rails/view_rendering.rb:1:in `<top (required)>'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68:in `require'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/rspec-rails-3.7.1/lib/rspec/rails.rb:9:in `<top (required)>'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:133:in `require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:133:in `rescue in require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:40:in `require'
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/action-cable-testing-0.1.2/lib/action_cable/testing/rspec.rb:4:in `<top (required)>'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:133:in `require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:133:in `rescue in require'
	from /home/christian/.rvm/rubies/ruby-2.3.4/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:40:in `require'
	from /home/christian/work/evopark/backend/spec/rails_helper.rb:17:in `<top (required)>'

After inserting that before requiring the action_cable/testing/rspec:

# spec/rails_helper.rb
require File.expand_path("../../config/environment", __FILE__)
require "action_cable/testing/rspec"

it went better, but still failed with:

⛔ WARNING: Sidekiq testing API enabled, but this is not the test environment.  Your jobs will not go to Redis.
**************************************************
/home/christian/work/evopark/backend/spec/rails_helper.rb:99:in `block in <top (required)>': uninitialized constant EmailSpec (NameError)
	from /home/christian/.rvm/gems/ruby-2.3.4@evopark/gems/rspec-core-3.7.0/lib/rspec/core.rb:98:in `configure'
	from /home/christian/work/evopark/backend/spec/rails_helper.rb:49:in `<top (required)>'

the line 99 there looks like:

# spec/rails_helper.rb
# line 49
RSpec.configure do |config|
  # ...
  #  line 99
  config.include EmailSpec::Helpers, type: :mailer
  # ...
end

Any hint is appreciated.

Connection unit-testing

There is no way to test ApplicationCable::Connection yet.

Why do we need? First, to test our authentication and identifiers resolving (in #connect method), secondly, to test our #disconnect code.

The tricky moment here is that we have to deal with HTTP requests somehow (like in integration tests), but we don't want to run the whole Action Cable server and use real WebSocket clients.

We can extend our ActionCable::Channel::TestCase with the following API:

def test_connect
  # What do we need? Connection path ('cause it may contain query params), headers and cookies
  connect path: "/cable?some_param=val", cookies: { user_id: "1123" }, headers: { "X-API-TOKEN" => 'abc' }
  
  assert connection.connected?
end

def test_connection_rejected
  assert_raises ActionCable::Connection::Authorization::UnauthorizedError do
    connect headers: { "X-API-TOKEN" => 'wrong_token' }
  end
end

def test_disconnect
  stub_connection(user_id: 123)

  assert_broadcast_on("users", event: "user_leave", id: "213) do
    disconnect
  end
end

Connection is not being closed after test

I am using ActionCable to consume RabbitMQ events once a client has connected and react on the messages via WS.

When testing with this gem, I noticed that the RMQ queues are not being removed afterwards(as they should be when connection is gone), which leads to nasty interactions.

Calling connection.close explicitly does not work, because close is referred to the @websocket internally, which just is a mock.

If an automatic solution after leaving the test case is not possible, I'd suggest a simple disconnect method, that handles the destruction of the connection object.

Name error for channel tests on rspec

Hi! I'm implementing a really simple chat feature with Rspec and while connection specs work fine I'm getting this error on the channel tests

NameError: uninitialized constant ChatChannel

This is my rspec code

RSpec.describe ChatChannel, type: :channel do

let(:user) { build_stubbed(:user) }

...

end

I've really no idea what's going on... Any help would be appreciated, thanks!

undefined method `assert_nothing_raised' in gems/actioncable-6.1.1/lib/action_cable/test_helper.rb:48:in `assert_broadcasts' (Rails 6.1, Rspec 4.0.2)

 1) CallController#create should emit RoomChannel
     Failure/Error:
       assert_broadcasts('room', 1) do
         post :create, params: {
           room_id: room.id,
           call: {
             a: 1
           }
         }
       end
     
     NoMethodError:
       undefined method `assert_nothing_raised' for #<RSpec::ExampleGroups::CallController::Create:0x00007fdb9866d750>
       Did you mean?  assert_routing
     # /Users/jason/.rvm/gems/ruby-2.6.3/gems/actioncable-6.1.1/lib/action_cable/test_helper.rb:48:in `assert_broadcasts'
     # ./spec/controllers/call_controller_spec.rb:23:in `block (3 levels) in <top (required)>'

Finished in 0.0991 seconds (files took 2.62 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/controllers/call_controller_spec.rb:20 # CallController#create should emit RoomChannel

my spec:

require 'rails_helper'


describe CallController do
  let!(:room) {create(:room)}
  describe "#create" do
    it "should return no content" do
      post :create, params: {
        room_id: room.id,
        call: {
          a: 1
        }
      }
      expect(response).to have_http_status(:no_content)
    end

    it "should emit RoomChannel" do
      assert_broadcasts('room', 1) do
        post :create, params: {
          room_id: room.id,
          call: {
            a: 1
          }
        }
      end
    end
  end
end

my code:

class CallController < ApplicationController

  skip_forgery_protection
  before_action :load_room


  def load_room
    @room = Room.find(params[:room_id])
  end

  def create
    head :no_content
    # logger.debug("CallController# create... calling: #{call_params.inspect}")


    RoomChannel.broadcast_to(@room, call_params)
  end

  private

  def call_params
    params.require(:call).permit(:type, :from, :to, :sdp, :candida)
  end
end

All I really need to do is assert that RoomChannel receives the broadcast message to the @room. I was going to do it with the old-style allow_any_instance_of(X).to receive(:___) but people recommend against this.

Also I tried doing it with double (which seems unnecessary), so simple built-in assertions would be great

I'm not sure why this bug is coming from assert_nothing_raised inside of # /Users/jason/.rvm/gems/ruby-2.6.3/gems/actioncable-6.1.1/lib/action_cable/test_helper.rb:48:in seems odd to me.

but if this gem is in Rails anyway now may I should report directly to Rails. I'm not 100% sure what I'm looking at or if if I've done something wrong above but it seems like this should work.

Run connection test as a part of rspec suite

I've got a working connection rspec file:

require "rails_helper"

RSpec.describe ApplicationCable::Connection, type: :channel do
  let!(:au) { create(:accounts_user) }
  let(:account) { au.account }
  let(:user) { au.user }

  it "successfully connects" do
    connect "/ws", cookies: {
      "user.id" => user.id,
      "user.expires_at" => 5.minutes.from_now
    }
    expect(connection.current_user).to eq user
  end

  it "rejects connection" do
    expect { connect "/ws" }.to have_rejected_connection
  end
end

But no matter where I place it, it's not running as a part of the whole rspec suite. I've followed the rspec instructions in the readme. Is something missing from those instructions?

Capybara RSpec vs. test adapter

Currently, setting test adapter globally breaks multisession Capybara Action Cable tests.

We can add a share context to use another adapter (e.g. async) for specific tests and (optionally) include it to type: :feature specs:

context "multiple sessions", action_cable: :async do
  scenario "all users see new question in real-time" do
    Capybara.using_session('author') do
      sign_in(user)
      visit questions_path
    end

    Capybara.using_session('guest') do
      visit questions_path
    end

    Capybara.using_session('author') do
      page.find("#add_question_btn").trigger('click')

      fill_in 'Title', with: 'Test question'
      fill_in 'Body', with: 'test text'
      click_on 'Save'
      expect(page).to have_content 'Test question'
      expect(page).to have_content 'test text'
    end

    Capybara.using_session('guest') do
      expect(page).to have_content 'Test question'
      expect(page).to_not have_content 'No questions found('
    end
  end
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.