Search code examples
ruby-on-railsrubyruby-on-rails-4rspecrspec-rails

Access to request object in request specs


How can I set the request headers before doing the request on a request spec?

I'm moving controller specs to request specs on my API using Rails. One thing I'm stuck on is that I have no access to a request object to allow the requests.

On my controller specs, I have access to a method I created which signs a specific user:

def sign_in(user)
  token = user.api_keys.first.token
  # note the request object being used in the next line
  request.env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Token.encode_credentials(token)
end

This works fine on controller specs, I can safely do:

before { sign_in(user) }
it { post :endpoint, params }

But on a request spec, the request object is not available. If I try to:

before { sign_in(user) }
it { post "/api/endpoint", params }

I get request on my helper method as nil.

I know I could do:

it { post "/api/endpoint", {"HTTP_AUTHORIZATION" => ... } }

But this seems a lot of clutter in the spec, specially compared to a controller spec.

I've tried using ActionDispatch::TestRequest::DEFAULT_ENV as suggested by this answer, but it didn't work too (I get a 401).


Solution

  • If you're not already using Rack::Test for this then you should be. Rack::Test is better suited than Capybara for testing API requests. It can be configured in your rspec/spec_helper.rb

    RSpec.configure do |config|
      # ...
      config.include Rack::Test::Methods
    end
    

    When you're configured to use Rack::Test, you can set headers before the request like so:

    it 'POST /api/enpoint authenticates successfully' do
      header 'Authorization', '...'
      post "/api/endpoint", params
      expect(last_response).to be_ok
    end
    

    This will be accessible in your controller as request.headers['HTTP_AUTHORIZATION'].

    The source code for this method can be found here - https://github.com/brynary/rack-test/blob/master/lib/rack/test.rb#L127-L141