Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set header in RSpec 3 request

Tags:

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.

like image 949
lightswitch05 Avatar asked Sep 12 '14 19:09

lightswitch05


1 Answers

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.

like image 156
Surya Avatar answered Sep 19 '22 13:09

Surya