20

I'd like to take advantage of the force_ssl feature in rails 3.1rc4.

class ApplicationController < ActionController::Base
  force_ssl
end

Problem is this breaks most/all of my existing RSpec controller specs. For example, this fails:

describe "GET 'index'" do
  it "renders the Start page" do
    get :index
    response.should render_template 'index'
  end
end

Instead of rendering the page the response is a 301 redirect to https://test.host/.

How can I change my specs to simulate an HTTPS GET/POST?

And do I have to change each test manually or is there an easier way? (I realise I could invoke force_ssl only in production, but that's not ideal. Really, I should be testing that force_ssl does indeed redirect to https:// when expected.)

naudster
  • 1,303
  • 1
  • 12
  • 9

4 Answers4

44

To instruct RSpec to make SSL requests in a controller spec use:

request.env['HTTPS'] = 'on'

In your example this looks like:

describe "GET 'index'" do
  it "renders the Start page" do
    request.env['HTTPS'] = 'on'
    get :index
    response.should render_template 'index'
  end
end
Will Koehler
  • 1,746
  • 22
  • 16
  • 1
    This does not work with RSpec 2.10. See here: https://github.com/rspec/rspec-rails/issues/534 – Robert Reiz Jul 16 '12 at 11:52
  • 3
    I am using rspe 2.11.0 and I am getting always this error message: "undefined method `env' for nil:NilClass". – Robert Reiz Oct 06 '12 at 13:41
  • 1
    Noting this is fixed with Rails-rspec 2.14 and Devise 3.0.0 https://github.com/rspec/rspec-rails/issues/534#issuecomment-21560832 – Brian Tol May 22 '14 at 13:03
  • 1
    Rails does not provide `request` in an integration test until after the action. https://github.com/rspec/rspec-rails/issues/596#issuecomment-7611241 – Darshan Rai Feb 16 '15 at 15:49
  • 8
    For actual _request_ specs (as opposed to the now deprecated controller specs), it works like this: `get "/path", headers: { "HTTPS" => "on" }`. The HTTPS header will be picked up by Rack::Request, telling everything down the line to consider the request to be an SSL/TLS one. – Carsten Jun 02 '17 at 15:08
2

For rspec 3 syntax, https://stackoverflow.com/a/28361428/1107433

get :index, protocol: 'https://'

There may be a slight different version that above code doesn't work. So use code below:

get :index, protocol: :https

Community
  • 1
  • 1
Thaichor Seng
  • 105
  • 10
1

For reference I'm currently running Rails 5.2.4 and RSpec 4.

The best solution as given in this answer is to change from using the _path helper to using the _url helper.

Then you can pass the protocol param to generate the HTTPS URL:

get login_url(protocol: :https)

Note that you cannot pass protocol to the _path helper:

# NOTE: THIS IS WRONG. Doesn't work with the `_path` helper:
# get login_path(protocol: :https)

An alternative option given in carp's comment above is to pass the "HTTPS" header with the fake request.

So if you're using strings/symbols or the _path helper for the request path:

get login_path, headers: {"HTTPS" => "on"}

get '/login', headers: {"HTTPS" => "on"}

Here is carp's full comment:

For actual request specs (as opposed to the now deprecated controller specs), it works like this: get "/path", headers: { "HTTPS" => "on" }. The HTTPS header will be picked up by Rack::Request, telling everything down the line to consider the request to be an SSL/TLS one.

Sly_cardinal
  • 12,270
  • 5
  • 49
  • 50
0

If you need SSL at all, anywhere in your application, you should use it everywhere in your application. Then all you need is to use the rack-ssl gem and add the Rack::SSL middleware to your config/environments/production.rb. No additional testing needed; no breakage; problem solved.

yfeldblum
  • 65,165
  • 12
  • 129
  • 169
  • Thanks @Justice, but the end result of the `rack-ssl` solution is no different to `force_ssl if Rails.env.production?` which is probably easier. In any case, isn't it better practice for test to mirror production, allowing you test for SSL redirects? This is what I'd prefer to do, but only if rspec is capable of simulating HTTPS requests. – naudster Jul 22 '11 at 04:56
  • 1
    Correct. But that's not the point. The point is that if your entire site is, in production, entirely SSL, then you don't need to write any test cases for that. Just require that gem and use that middleware in your production environment, and you're good to go. Remember, this only works if your entire site is over SSL, not just some pieces of your site. But, as a general security principle, if any part of your site needs to be over SSL, then *all* of your site needs to be over SSL. – yfeldblum Jul 22 '11 at 05:00
  • This unfortunately is not a universally true principle. It's more on the order of 50%. Quite often you want to have public resources that are cacheable for performance and scalability reasons. If the majority of your traffic is public unauthenticated traffic (which is quite a common traffic profile) then universal SSL may be a higher cost than you want to pay. – gtd Mar 08 '13 at 12:14
  • There are various attacks against SSL sessions where the vulnerability is the fact that the page with the link to the SSL-only page is *not* itself SSL-protected. There are various other attacks against SSL sessions which target content that is included into the page, such as JavaScript, which included content is itself not SSL-protected. I am not aware of any legitimate exception to the principle, including for caching. High-performance and cached delivery of public resources can be done with SSL. – yfeldblum Mar 08 '13 at 14:19