I'm trying to set the header for some RSpec requests that require authentication. The header is ACCESS_TOKEN
. No matter how I attempt to set the header, it never gets set. I know the app works because I can manually test it, I just cant get rspec tests to work. See the full source code & tests for this problem here: https://github.com/lightswitch05/rspec-set-header-example
Since authentication is used in most of my request specs, I've created support helper module to retrieve an access token and set it in the header. Below is the summary of how I'm trying to set the header, see everything I've tried in the full source
# my_app/spec/support/session_helper.rb module SessionHelper def retrieve_access_token post api_v1_session_path({email: '[email protected]', password: 'poor_password'}) expect(response.response_code).to eq 201 expect(response.body).to match(/"access_token":".{20}"/) parsed = JSON(response.body) token = parsed['access_token']['access_token'] @request.headers['HTTP_ACCESS_TOKEN'] = token end end
an example request spec that uses this helper and should work, but always fails because the header never gets set:
# my_app/spec/requests/posts_spec.rb # ... context "create" do it "creates a post" do retrieve_access_token post = FactoryGirl.build(:post) post api_v1_posts_path( post: { title: post.title, content: post.content } ) expect(response.body).to include('"id":') expect(response.body).to include('"title":"' + post.title + '"') expect(response.body).to include('"content":"' + post.content + '"') expect(response.response_code).to eq 201 end end
I know I can manually set the header in the individual get
and post
requests - but that is not a maintainable solution for API-wide authorization. Imagine having to change every test if the header name changed slightly.
Note: This answer is based on what you seem to be calling api_v1_session_path
with post
request to SessionsController
for every spec you're trying to run in your requests specs.
There are two ways to solve the issue I figured you have here.
Solution #1 - Either you create another helper method in your SessionHelper
or in some other helper file called support/requests_helper.rb(however you prefer). I'd create another helper in support/requests_helper.rb:
module RequestsHelper def get_with_token(path, params={}, headers={}) headers.merge!('HTTP_ACCESS_TOKEN' => retrieve_access_token) get path, params, headers end def post_with_token(path, params={}, headers={}) headers.merge!('HTTP_ACCESS_TOKEN' => retrieve_access_token) post path, params, headers end # similarly for xhr.. end
then in rails_helper.rb:
# Include the sessions helper config.include SessionHelper, type: :request # Include the requests helper config.include RequestsHelper, type: :request
change session_helper.rb:
# my_app/spec/support/session_helper.rb module SessionHelper def retrieve_access_token post api_v1_session_path({email: '[email protected]', password: 'poor_password'}) expect(response.response_code).to eq 201 expect(response.body).to match(/"access_token":".{20}"/) parsed = JSON(response.body) parsed['access_token']['access_token'] # return token here!! end end
Now, you can change your all requests specs like this:
describe Api::V1::PostsController do context "index" do it "retrieves the posts" do get_with_token api_v1_posts_path expect(response.body).to include('"posts":[]') expect(response.response_code).to eq 200 end it "requires a valid session key" do get api_v1_posts_path expect(response.body).to include('"error":"unauthenticated"') expect(response.response_code).to eq 401 end end end
Solution #2 - Change specs/factories/access_token_factory.rb to:
FactoryGirl.define do factory :access_token do active true end # can be used when you want to test against expired access tokens: factory :inactive_access_token do active false end end
Now, change your all requests specs to use access_token
:
describe Api::V1::PostsController do context "index" do let(:access_token){ FactoryGirl.create(:access_token) } it "retrieves the posts" do # You will have to send HEADERS while making request like this: get api_v1_posts_path, nil, { 'HTTP_ACCESS_TOKEN' => access_token.access_token } expect(response.body).to include('"posts":[]') expect(response.response_code).to eq 200 end it "requires a valid session key" do get api_v1_posts_path expect(response.body).to include('"error":"unauthenticated"') expect(response.response_code).to eq 401 end end end
I'd go with "Solution #1" as it removes a burden of making you remember to send HTTP_ACCESS_TOKEN
in headers every time you want to make such requests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With