Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rails 5 / Rspec 3.5.0 -- setting request headers? #1655

Closed
ledbettj opened this issue Jul 5, 2016 · 42 comments · Fixed by #2229
Closed

Rails 5 / Rspec 3.5.0 -- setting request headers? #1655

ledbettj opened this issue Jul 5, 2016 · 42 comments · Fixed by #2229

Comments

@ledbettj
Copy link

ledbettj commented Jul 5, 2016

Hi,

I had a spec working under Rails 4 / Rspec 3.4.4 that looked like this:

it 'does something with the referrer' do
  allow(request).to receive(:referrer).and_return('aol.com')
  expect(Foo).to receive(:bar).with('aol.com')
  get :endpoint
end

After upgrading, it looks like request.referrer is not stubbed correctly and request.referrer in the controller returns nil.

I tried rewriting like so:

get :endpoint, { params: {} }, headers: { 'HTTP_REFERER' => 'aol.com' }

But I wasn't able to get that header, or actually any header passed through to the controller.

Any suggestions?

@ledbettj
Copy link
Author

ledbettj commented Jul 5, 2016

Setting the header in request.env works -- is that the recommended way to accomplish this?

@JonRowe
Copy link
Member

JonRowe commented Jul 5, 2016

Try: get :endpoint, params: {}, headers: { 'HTTP_REFERER' => 'aol.com' }

@fables-tales
Copy link
Member

@ledbettj
Thanks for the issue. We need a little more detail to be able to reproduce this.

Could you please provide us with a rails app that we can clone that demonstrates the issue. Specifically it'd be great if

  1. you could rails new an application and commit
  2. make all the changes necessary to reproduce the issue and commit

then, provide us with a description of how to clone your application and reproduce the issue.

Thanks :)

@ledbettj
Copy link
Author

ledbettj commented Jul 25, 2016

I've pushed a basic example here.

ApplicationController#index simply renders the HTTP referrer as JSON. The specs for this action try to set the referrer in several ways; only the last -- setting request.env -- works.

git clone https://github.com/ledbettj/rspec-repro.git repro
cd repro
bundle
bundle exec rake spec

Output:

Failures:

  1) ApplicationController when stubbing referrer sets the referrer correctly
     Failure/Error: expect(body['from']).to eq(aol)

       expected: "aol.com"
            got: nil

       (compared using ==)
     # ./spec/controllers/application_controller_spec.rb:11:in `block (3 levels) in <top (required)>'

  2) ApplicationController when passing referrer as a hash sets the referrer correctly
     Failure/Error: expect(body['from']).to eq(aol)

       expected: "aol.com"
            got: nil

       (compared using ==)
     # ./spec/controllers/application_controller_spec.rb:18:in `block (3 levels) in <top (required)>'

  3) ApplicationController when passing in two hashes sets the referrer correctly
     Failure/Error: expect(body['from']).to eq(aol)

       expected: "aol.com"
            got: nil

       (compared using ==)
     # ./spec/controllers/application_controller_spec.rb:25:in `block (3 levels) in <top (required)>'

Finished in 0.04602 seconds (files took 2.82 seconds to load)
4 examples, 3 failures

Let me know if anything else would help.

@richddr
Copy link

richddr commented Jul 28, 2016

request.env did not work for me. Any update on this?

@fables-tales
Copy link
Member

@richddr looks like nobody from the RSpec core team has had a chance to look at this yet. If you really need a fix to this, you're more than welcome to use the reproduction case above as a starting point for debugging and see if you can fix the problem :)

@sebastian-julius
Copy link

I worked around this like so. Maybe it helps you, too. Not the prettiest solution but effective.

  let(:headers) { some: "header" }
  # ...
  request.headers.merge! headers
  post :endpoint, params: params

@monorkin
Copy link

monorkin commented Oct 4, 2016

I tried to fix this issue but don't see anything wrong with the way it currently is. (I don't know if this is a regression)

Just to explain as to why this is happening.

Regarding the stubbed method. If you take a look at ActionController::TestCase::Behaviour you will notice that all HTTP methods (get, post, delete, ...) get funneled into the process method. The process method builds a new TestRequest object with the request.env object from your test. That's why stubbed methods won't work.

Regarding the remaining two options - passing the headers as params. This also won't work. If you take a look at the above-mentioned class you can notice that only the session, flash, method, body, xhr params get processed and all others are ignored.

This leaves you with the option to edit request.env direcly or through request.headers as @sebastian-julius outlined.

@richddr
Copy link

richddr commented Oct 4, 2016

Sorry, forgot to post an update here. I also ended up working around it the same as @sebastian-julius did.

@douglaslise
Copy link

douglaslise commented Oct 23, 2016

You cannot stub request because it is recreated in this line of actionpack/lib/action_controller/test_case.rb:

  @request = TestRequest.new scrub_env!(@request.env), @request.session, @controller.class

So, the request seen in the controller action is another instance of the request accessible/modified in the spec, and neither all the env part are kept.

@dcrockwell
Copy link

dcrockwell commented Dec 8, 2016

I expected this to work:

get :create, headers: { Key: "SomeKey" }

Ended up doing this, per @sebastian-julius's workaround:

headers = { :Key => @api_key.token }
request.headers.merge! headers
get :create

@dpsk
Copy link

dpsk commented Jan 9, 2017

For me the issue is that req.headers['CustomHeader'] is nil while req.env['CustomHeader'] contains required value. For production environment it's the other way around with Rails 5.0 and rspec 3.5.0.
Am i missing something?

@pawandubey
Copy link

I faced this issue today and I dug a little. It seems that for request specs, rspec-rails correctly leads us to ActionDispatch::IntegrationTest::Behavior as seen here:

include ActionDispatch::IntegrationTest::Behavior

But for controller tests, it leads us to the old and now deprecated ActionController::TestCase::Behavior, as seen here:

include ActionController::TestCase::Behavior

ActionDispatch::IntegrationTest has support for extracting the header key and setting headers normally works, but breaks in ActionController::TestCase and hence it works in request specs, but not in controller specs.

I think this is a bug and the core team should take a look at it.

jayhardee9 pushed a commit to bmic-development/sparc-request that referenced this issue Feb 15, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
jayhardee9 pushed a commit to bmic-development/sparc-request that referenced this issue Feb 15, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
jayhardee9 pushed a commit to bmic-development/sparc-request that referenced this issue Feb 21, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
wtholt pushed a commit to bmic-development/sparc-request that referenced this issue Feb 24, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
wtholt pushed a commit to bmic-development/sparc-request that referenced this issue Mar 2, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
wtholt pushed a commit to bmic-development/sparc-request that referenced this issue Mar 3, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
wtholt pushed a commit to bmic-development/sparc-request that referenced this issue Mar 8, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
wtholt pushed a commit to bmic-development/sparc-request that referenced this issue Mar 15, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
wtholt pushed a commit to bmic-development/sparc-request that referenced this issue Mar 16, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
wtholt pushed a commit to bmic-development/sparc-request that referenced this issue Mar 17, 2017
…ller specs.

Instead, I’m wrapping request.referrer usage in a method that can be
stubbed out. See rspec/rspec-rails#1655
@fragoulis
Copy link

fragoulis commented Oct 26, 2017

@KevinColemanInc between requests no, and why should it? It resets for each test, and each test should have one request.

@falegk
Copy link

falegk commented Dec 29, 2017

The only thing that worked for me. (rails: 5.1.4 & rspec-rails 3.7.2)

before(:all) do
    header 'Authorization', "Bearer <token>"
    get '/my/endpoint'
end

@obie
Copy link

obie commented Mar 9, 2018

I ended up here trying to figure out how to mock the request.domain ... since doubles are not allowed in global RSpec context, I just brute forced it by overriding the method on TestRequest like this:

ActionController::TestRequest.class_eval do
  def domain
    "example.com"
  end
end

m-an added a commit to m-an/barong that referenced this issue Mar 19, 2018
  + Fixed expiring time in Doorkeeper-JWT

  Unfortunetely, RSpec has an issue and can not send HTTP header, so I
  haven't finished tests. Link to the github issue: rspec/rspec-rails#1655
  Any solutions, provided by github users in issue comments hadn't
  helped me.
m-an added a commit to m-an/barong that referenced this issue Mar 20, 2018
  + Fixed expiring time in Doorkeeper-JWT
  + implemented allow_simple_login feature

  Unfortunetely, RSpec has an issue and can not send HTTP header, so I
  haven't finished tests. Link to the github issue: rspec/rspec-rails#1655
  Any solutions, provided by github users in issue comments hadn't
  helped me.
@m-an

This comment has been minimized.

@mikegee
Copy link

mikegee commented Mar 21, 2018

@andreymakovenko yup, it works in request specs.

@ArielAleksandrus
Copy link

why so many downvotes on @andreymakovenko reply?

@mikegee
Copy link

mikegee commented May 24, 2018

@ArielAleksandrus this issue is about controller specs not request specs.

@crusadergo
Copy link

crusadergo commented Jun 9, 2018

for example:
request.headers.merge!(valid_headers)
post :create, params: { order: order.attributes }

@MiguelCorti
Copy link

MiguelCorti commented Oct 10, 2018

Hello, so... nothing works for me :p I've tried everything, I think I'm doing something wrong. I'm using rails 5.2.0 and rspec-rails 3.8.0 and I tried doing:

  1. header 'access-token', my_token
  2. request.headers['access-token'] = my_token
  3. request.env['HTTP_ACCESS-TOKEN'] = my_token
  4. request.headers.merger!({'access-token': my_token})
  5. get my_path, :headers => {'access-token' => token}
  6. get my_path, nil, :headers => {'access-token' => token}
  7. get my_path, params: {}, :headers => {'access-token' => token}

NOTHING WORKS, am I dumb?
1), 5), 6) and 7) simply don't set the headers, and the request returns a 401 Unauthorized instead of 200 OK
2), 3) and 4) don't work cause it says request needs more arguments (1..2 arguments), meaning it's a function...

Here's the spec file:

require 'spec_helper'

describe Transaction, :type => :api do
	context 'get all balances' do
		before do
			user = User.create(
				name: 'John Smith', 
				email: '[email protected]', 
				password: '123456', password_confirmation: '123456',
				mobile_number: '999199389')
			client, token = user.create_token
			# header 'access-token', token
			# header 'client', client
			# header 'uid', user.email
			# request.headers['Access-Token'] = token
			# request.headers['Client'] = client
			# request.headers['Uid'] = user.email
		        # headers = { :access_token => token, :client => client, :uid => user.email }
		        # request.headers.merge! headers
			get '/allbalances', :headers => {'access-token' => token, 'client' => client, 'uid' => user.email}
		end

		it 'responds with OK status' do
			byebug
			expect(last_response.status).to eq 200
		end
	end
end

Someone help me pleassse

@JonRowe
Copy link
Member

JonRowe commented Oct 11, 2018

Dependant on what your type: :api triggers here, will depend on how you send headers

@MiguelCorti
Copy link

Sorry, my bad, I'm using controller type now, but the main issue was something else, unrelated do Rspec. Everything works now! Thanks!

@lukad
Copy link

lukad commented Nov 20, 2018

Before you post your solution please make sure that you understand the difference between request specs and controller specs. This issue is about controller specs.
Reading all the replies that aren't helping is really frustrating.

@alvesoaj
Copy link

alvesoaj commented Dec 18, 2018

@fnmendez works well (in controller specs), I recommend to check in controller if simple "puts" is the values are right placed; I spend 5 hours thinking that the solution not works, but, I was not the right values in models (fixtures)

@dennym
Copy link

dennym commented Mar 11, 2019

get '/foobar.json', {}, { 'HTTP_SESSION_ID' => 'XXXX', .... }

Using this works for controller/feature specs. The only thing you have to make sure is that the headers are on the second position of the args. That's why the empty hash.
Since they get utilized here https://github.com/rails/rails/blob/e2fcb2b4aec69e10a01cebfe51bbd280ce6d5a93/actionpack/lib/action_controller/test_case.rb#L602

As @pawandubey its the different module request and controller specs are using.

Unfortunatly when you use the snippet like top one rubocop will complain because
of Rails/HttpPositionalArguments: Use keyword arguments instead of positional arguments for http call
This also a topic over at rubocop/rubocop#5888. But it seems stall.

@kiecoorp
Copy link

For HTTP_REFERER use symbol and not string !

post "/api/v1/kpulse", params: params, headers: { HTTP_REFERER: 'kpulse.fr' }

@salmagomaa
Copy link

salmagomaa commented Jul 11, 2019

This solution worked for me too
https://robert-reiz.com/2013/08/09/http_referer-for-rspec-is-missing/

post "/sessions", {:session => {:email => user.email, :password => user.password}}, {"HTTPS" => "on", 'HTTP_REFERER' => '/signin'}

@fabioaraujo121
Copy link

fabioaraujo121 commented Sep 26, 2019

For Rails 5.2.3 and Rspec 3.8
Just used the request.headers.merge!(valid_headers)

let(:valid_attributes) {
  {to_wallet: "wallet", value: 100.00}
}

let(:valid_headers) { { HTTP_USERTOKEN: "Token12345678"} }

it "creates a new Transaction" do
  request.headers.merge!(valid_headers) # This line right here!!!
  expect {
    post :create, params: {transaction: valid_attributes}
  }.to change(Transaction, :count).by(1)
end

@Schwad
Copy link

Schwad commented Nov 5, 2019

Hello from 2019!

Just want to confirm I came here trying to set headers on a controller test and that @sebastian-julius ' approach (though I slightly modified it) worked great for me! Thank you! :)

@mrothenbuhler
Copy link

For me the issue is that req.headers['CustomHeader'] is nil while req.env['CustomHeader'] contains required value. For production environment it's the other way around with Rails 5.0 and rspec 3.5.0.
Am i missing something?

Same! It was request.headers['myHeader'] was missing. Man that took me hours.

@pirj
Copy link
Member

pirj commented Nov 26, 2019

Are there any volunteers to update the doc and add feature test similar to the cookie one?

@7wapnil
Copy link

7wapnil commented Aug 11, 2021

This works for me on rspec 3.8.0

it 'return 200' do
  auth = 'Basic ' + Base64.strict_encode64('username:password')
  params = { foo: 'bar' }
  
  header('Authorization', auth)
  
  get '/baz', params

  expect(last_response.status).to eq 200
end

Did'nt work

@imnithin
Copy link

imnithin commented Feb 1, 2023

This might work - on rspec-rails 3.8 works

request.env['HTTP_REFERER'] = 'aol.com'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.