0

I'm attempting to write a request spec for a Rails app in API-only mode. In the UsersController, a before_action sets the @user using @uid (the primary key), which I'm attempting to assign in the spec.

User.uid is a custom primary key, User.find() works fine in development and production (as does everything else, I'm trying to learn how to test here). This is the error I receive in test:

Failure/Error @user = User.find(@uid)
 ActiveRecord::RecordNotFound:
 Couldn't find User without an ID
//controller
module Api
  module V1
    class UsersController < ApplicationController
      before_action :set_user, only: %i[show update]

      def show
        render json: @user
      end
    private
      def set_user
        @user = User.find(@uid)
      end
   end
end
// request spec
require 'rails_helper'
require 'test_helper'

describe 'get user route' do
  let!(:user) { FactoryBot.create(:user) }
  before :each do
    allow_any_instance_of(ApplicationController).to receive(:authorize).and_return(true)
    @uid = user.uid
  end
  
  before { get '/api/v1/user' }

  it 'returns status code 200' do
    expect(response).to have_http_status(:success)
  end
end

"authorize" is a before action on the ApplicationController that assigns @uid. I'm skipping it and trying to assign @uid to the created user's uid. There must be a better way to do this as well...

edit: I'm using Ruby 2.7.1 and the rspec-rails gem 4.0.1

The @uid is being set with a method in the ApplicationController which is used as a before_action. It's using a library to decode a firebase jwt and return an auth_user object which contains the uid.

class ApplicationController < ActionController::API
  def authorize
    auth_user = FirebaseIdToken::Signature.verify(params[:idToken])
    @uid = auth_user ? auth_user['user_id'] : nil
    render json: {}, status: 401 if @uid.nil?
  end
end

I've read some posts that say that you lose the ability to set the instance variables by using a request spec (can't find this in the docs), because these types of specs are supposed to mimic a real scenario as much as possible. The controller instance variable is only available after the request so I'm not sure if it's possible at all to assign a controller's instance variable from a request spec.

1 Answer 1

1

The way you have set @uid in spec, will not be available in controller. you need to set instance variable as follow be accessible in controller.

controller.instance_variable_set(:@uid, user.uid)
require 'rails_helper'
require 'test_helper'

describe 'get user route' do
  let!(:user) { FactoryBot.create(:user) }
  before :each do
    allow_any_instance_of(ApplicationController).to receive(:authorize).and_return(true)
    controller.instance_variable_set(:@uid, user.uid)
  end
  
  before { get '/api/v1/user' }

  it 'returns status code 200' do
    expect(response).to have_http_status(:success)
  end
end
Sign up to request clarification or add additional context in comments.

6 Comments

using this approach I get the following error: NameError: `uid' is not allowed as an instance variable name
with controller.instance_variable_set(:@uid, user.uid) the error is FrozenError: can't modify frozen NilClass: nil
Which ruby and rspec version you are using? also could you please provide the snippet where you are setting @uid?
I answered your questions by editing the question. I appreciate your help but I'm not sure this is possible
@Meursault have you tried the steps given in firebase_id_token Gem for specs? - github.com/fschuindt/firebase_id_token#rails-test
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.