RSpec Request - How to set http authorization header for all requests

17,365

Solution 1

I don't think you should depend on the header if you are not testing the header itself, you should stub the method that checks if the HTTP_AUTORIZATION is present and make it return true for all specs except the spec that tests that particular header

something like... on the controller

Controller...
  before_filter :require_http_autorization_token

  methods....

  protected
  def require_http_autorization_token
    something
  end

on the spec

before(:each) do
  controller.stub!(:require_http_autorization_token => true)
end

describe 'GET user' do
  it 'returns something' do
    #call the action without the auth token
  end

  it 'requires an http_autorization_token' do
    controller.unstub(:require_http_autorization_token)
    #test that the actions require that token
  end
end

that way one can forget the token and test what you really want to test

Solution 2

To set it in a before hook you need to access it like

config.before(:each) do
  controller.request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.encode_credentials('mytoken')
end

I too hated the giant hash, but preferred to be explicit in authorizing the user in different steps. After all, it's a pretty critical portion, and . So my solution was:

#spec/helpers/controller_spec_helpers.rb
module ControllerSpecHelpers
  def authenticate user
    token = Token.where(user_id: user.id).first || Factory.create(:token, user_id: user.id)
    request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.encode_credentials(token.hex)
  end
end

#spec/spec_helper.rb
RSpec.configure do |config|
  ...
  config.include ControllerSpecHelpers, :type => :controller

then I can use it like so

describe Api::V1::Users, type: :controller do
  it 'retrieves the user' do
    user = create :user, name: "Jane Doe"
    authorize user
    get '/v1/users/janedoe.json'
  end
end

I find this great for testing different authorization levels. Alternatively, you could have the helper method spec out the authorize function and get the same result, like so

#spec/helpers/controller_spec_helpers.rb
module ControllerSpecHelpers
  def authenticate
    controller.stub(:authenticate! => true)
  end
end

However, for ultimate speed and control, you can combine them

#spec/helpers/controller_spec_helpers.rb
module ControllerSpecHelpers
  def authenticate user = nil
    if user
      token = Token.where(user_id: user.id).first || Factory.create(:token, user_id: user.id)
      request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.encode_credentials(token.hex)
    else
      controller.stub(:authenticate! => true)
    end
  end
end

and then authorize entire blocks with

#spec/spec_helper.rb
...
RSpec.configure do |config|
  ...
  config.before(:each, auth: :skip) { authenticate }

#**/*_spec.rb
describe Api::V1::Users, type: :controller do
  context 'authorized', auth: :skip do
    ...

Solution 3

I know that this question has already been answered but here's my take on it. Something which worked for me:

request.headers['Authorization'] = token

instead of:

request.env['Authorization'] = token

Solution 4

This is another way to do it if you are doing a post.

@authentication_params = { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(Temp::Application.config.api_key) }

expect { post "/api/interactions", @interaction_params, @authentication_params }.to change(Interaction, :count).by(1)

Note interaction_params is just a json object I am passing in.

Share:
17,365
Buzz
Author by

Buzz

Updated on June 05, 2022

Comments

  • Buzz
    Buzz almost 2 years

    I'm using rspec request to test a JSON API that requires an api-key in the header of each request.

    I know I can do this:

    get "/v1/users/janedoe.json", {}, { 'HTTP_AUTHORIZATION'=>"Token token=\"mytoken\"" }
    

    But it is tedious to do that for each request.

    I've tried setting request.env in the before block, but I get the no method NilClass error since request doesn't exist.

    I need some way, maybe in the spec-helper, to globally get this header sent with all requests.

  • slhck
    slhck almost 5 years
    Is your code missing some brackets? authenticate user = nil, and you have used authenticate and authorize ambiguously.