How do I change the default "www.example.com" domain for testing in rails?
Asked Answered
S

11

79

I have a rails application which acts differently depending on what domain it's accessed at (for example www.myapp.com will invoke differently to user.myapp.com). In production use this all works fine but my test code always sees a hostname of "www.example.com".

Is there a clean way of having a test specify the hostname it's pretending to access?

Sabulous answered 1/3, 2009 at 0:5 Comment(0)
G
35
@request.host = 'user.myapp.com'
Gawk answered 1/3, 2009 at 2:33 Comment(3)
In a controller spec, I get an error when trying to use host!. Setting @request.host like the answer suggests worked though.Hangout
And how do I use it with RSpec? I mean, where do I configure it? many thanks!Occasionalism
@Occasionalism : Configuration in RSpec: Have a look at https://mcmap.net/q/262917/-capybara-with-subdomains-default_host. Capybara.app_host = "http://example.com" has worked for me.Ellene
A
126
  • Integration/Request Specs (inheriting from ActionDispatch::IntegrationTest):

     host! 'my.awesome.host'
    

See the docs, section 5.1 Helpers Available for Integration Tests.

alternatively, configure it globally for request specs at spec_helper.rb level:

RSpec.configure do |config|
  config.before(:each, type: :request) do
    host! 'my.awesome.host'
  end
end
  • Controller Specs (inheriting from ActionController::TestCase)

     @request.host = 'my.awesome.host'
    

See the docs, section 4.4 Instance Variables Available.

  • Feature Specs (through Capybara)

     Capybara.default_host = 'http://my.awesome.host'
     # Or to configure domain for route helpers:
     default_url_options[:host] = 'my.awesome.host'
    

From @AminAriana's answer

  • View Specs (inheriting from ActionView::TestCase)

     @request.host = 'my.awesome.host'
    

...or through RSpec:

    controller.request.host = 'my.awesome.host'

See the rspec-rails view spec docs.

Aversion answered 13/3, 2015 at 16:36 Comment(4)
Thanks to rails 5 and switching from ActionController::TestCase to ActionDispatch::IntegrationTest this top Integration Specs is invaluable to those of us with legacy multi-tennant apps. This was the first answer that showed how to get @request.host easily converted into an older suite (by using host!). Reading the docs this didn't jump out at me but here we are, and thanks!Birnbaum
Where does that default_url_options[:host] = "my.awesome.host" go?Outgo
I don't recall well, it's been a while. Does it not work if you place it in the main body of a feature spec?Aversion
For anyone using type: :request tests, I discovered that the path and url helpers were causing the problems with "example.com". It looks like a string should be used instead of the helpers.Hallel
G
35
@request.host = 'user.myapp.com'
Gawk answered 1/3, 2009 at 2:33 Comment(3)
In a controller spec, I get an error when trying to use host!. Setting @request.host like the answer suggests worked though.Hangout
And how do I use it with RSpec? I mean, where do I configure it? many thanks!Occasionalism
@Occasionalism : Configuration in RSpec: Have a look at https://mcmap.net/q/262917/-capybara-with-subdomains-default_host. Capybara.app_host = "http://example.com" has worked for me.Ellene
S
33

Feature specs

In Feature specs, host! has been deprecated. Add these to your spec_helper.rb:

# Configure Capybara expected host
Capybara.app_host = "http://test.domain"

# Configure actual routes host during test
before(:each) do
  default_url_options[:host] = <myhost>
end

Request specs

In Request specs, keep using host! :

host! "test.domain"

Alternatively refactor it in before(:each) blocks, or configure it globally for request specs at spec_helper.rb level:

RSpec.configure do |config|
  config.before(:each, type: :request) do
    host! "test.domain"
  end
end
Stealage answered 23/2, 2015 at 21:27 Comment(4)
Note: It seems like Capybara.app_host needs to include the protocol. E.g. "http://test.domain" rather than just "test.domain".Corenda
how to include the port?Rosalynrosalynd
The amount of time wasted on this is ridiculous. Thanks. This is about the only thing that worked. Why in the world this wouldn't "just work" by defining the default url options in Rails config/environments/test.rb is beyond me.Abrasive
@MaximKrizhanovsky Disregard host! and use something like setup do Rails.application.routes.default_url_options = { port: "3000", host: "localhost" } endAbnegate
C
9

For Rspec Request specs, use before(:each) { host! 'example.com' }

See more at: https://relishapp.com/rspec/rspec-rails/v/3-6/docs/request-specs/request-spec https://github.com/rspec/rspec-rails/issues/1662#issuecomment-241201056

Chateau answered 5/4, 2018 at 20:3 Comment(0)
P
5

I believe you can modify the HTTP_HOST or SERVER_NAME environment vars to change the request that goes to the router:

ENV['SERVER_NAME'] = "user.myapp.com"

See raw_host_with_port in actionpack/lib/action_controller/request.rb.

Possessive answered 1/3, 2009 at 2:23 Comment(0)
B
5

Another thing to remember is to make sure to use the correct session instance so that you can properly encapsulate the url helpers.

Integration tests provide you with a default session. You can call all session methods directly from your tests

test "should integrate well" do
  https!
  get users_path
  assert_response :success
end

All these helpers are using the default session instance, which if not changed, goes to "www.example.com". As has been mentioned the host can be changed by doing host!("my.new.host")

If you create multiple sessions using the open_session method, you must ALWAYS use that instance to call the helper methods. This will properly encapsulate the request. Otherwise rails will call the default session instance which may use a different host:

test "should integrate well" do
  sess = open_session
  sess.host! "my.awesome.host"
  sess.get users_url             #=> WRONG! will use default session object to build url.
  sess.get sess.users_url        #=> Correctly invoking url writer from my custom session with new host.
  sess.assert_response :success
end

If you intended to use the default session object, then you'll have to alter that host as well:

test "should integrate well" do
  sess = open_session
  sess.host! "my.awesome.host"
  host! sess.host              #=> Set default session host to my custom session host.
  sess.get users_url
end 
Breaker answered 24/9, 2013 at 15:33 Comment(1)
I've been stuck on this problem all day and adding before(:each) { https! } is the only thing that worked. Wish I could upvote this more.Taradiddle
T
3

@request.host = 'user.myapp.com' is not right. should use host!('user.myapp.com')

Tripartite answered 18/6, 2010 at 3:29 Comment(2)
This syntax did not work for me. Neither did the accepted answer. The only thing that worked as host!('newhostname.com').Activist
Of course you need to put the string of the host name in single or double quotes.Kazan
L
1

I tried many variations of @request.host, host!, and post path, args, {'SERVER_NAME' => my_secret_domain} without success, both as controller tests and feature tests. Very aggravating, as so many others reported success with those approaches.

The solution for me was:

request.headers["SERVER_NAME"] = my_secret_domain
post path, args

I'm running ruby 2.1.5p273, rspec 3.1.7 and Rails 4.2.0

Longish answered 8/4, 2015 at 20:38 Comment(0)
H
1

None of the ways suggested in other answers at the point worked for me. This worked:

Capybara.configure { |config| config.default_host = "my.domain.com" }
Handkerchief answered 19/2, 2018 at 11:43 Comment(0)
J
0

Yet another answer:

request.host = "user.myapp.com"

I know it resembles the correct answer, but please bear with me. I don't like assignment operation in test just to set things up, I'd prefer an explicit stub. Interestingly, stubbing like this won't work:

allow(request).to receive(:host).and_return("user.myapp.com")

I personally prefer stubbing over assignment, that way I get 2 benefit, one is that it will be validated by rspec's verify double, second is that it is explicitly saying that is a stub, not part of the test excercise.

Jacqui answered 6/4, 2017 at 7:7 Comment(1)
The difference at the start of this answer is you use a local variable or method call instead of an instance variable. Then you go on to talk about stubbing instead of assignment. I would suggest against that style because although it seems you're getting extra checks, you're actually adding an unnecessary dependency on the stubbing framework. It should be clear here what's setup and what's not. The test should fail if setting the domain does not work. Or just add an assert that the domain is what you expected.Thigmotaxis
O
0

in test_helper.rb

class ActionDispatch::IntegrationTest
  before { host! Rails.application.credentials[Rails.env.to_sym][:host] }
end
Oecology answered 2/3, 2024 at 0:58 Comment(1)
Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?Family

© 2022 - 2025 — McMap. All rights reserved.