2
votes

Rails 5.2 I have the following ApplicationCable::Connection ruby file:

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
        message = "The user is not found. Connection rejected."
        logger.add_tags 'ActionCable', message  
        self.transmit error: message 
        reject_unauthorized_connection
      end
    end
  end
end

I want to test this setup and and using the following RSpec test:

require 'rails_helper.rb'

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

Which fails with:

Failure/Error: if verified_user = env['warden'].user

NoMethodError: undefined method `[]' for nil:NilClass

So I want to stub out the env['warden'].user code and return an id of 325. I tried the following:

allow(env['warden']).to receive(:user).and_return(325)

But that produced the following error:

undefined local variable or methodenv'

How can I test this class?

2

2 Answers

5
votes

Try this:

require 'rails_helper.rb'

RSpec.describe ApplicationCable::Connection, type: :channel do

   let(:user)    { instance_double(User, id: 325) }
   let(:env)     { instance_double('env') }

  context 'with a verified user' do

     let(:warden)  { instance_double('warden', user: user) } 

    before do
      allow_any_instance_of(ApplicationCable::Connection).to receive(:env).and_return(env)
      allow(env).to receive(:[]).with('warden').and_return(warden)
    end

    it "successfully connects" do
      connect "/cable", headers: { "X-USER-ID" => 325 }
      expect(connect.current_user.id).to eq 325
    end

  end

  context 'without a verified user' do

    let(:warden)  { instance_double('warden', user: nil) }

    before do
      allow_any_instance_of(ApplicationCable::Connection).to receive(:env).and_return(env)
      allow(env).to receive(:[]).with('warden').and_return(warden)
    end

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

  end
end
1
votes

Here's a good explanation for your issue https://stackoverflow.com/a/17050993/299774

The question was about controller tests, but it's really similar.

I also don't think you should access low level env['warden'] in your controller. What if the gem authors decide to change that - you'd have to fix your app. Probably warden objects are initialized with this config, and there should be an object available (just not necessary when you run your specs - as described in the link above).