I am using RSpec and Capybara for my feature specs on my Rails 5.1 app. I want to fake the request IP to '1.2.3.4' for a single spec.
I've tried the following with Poltergeist...
before do
page.driver.add_headers 'REMOTE_HOST' => '1.2.3.4'
end
However, placing a pry in my controller I see that request.headers['REMOTE_HOST'] is 127.0.0.1.
I solved this by stubbing ActionDispatch::Request#remote_ip
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip) { '1.2.3.4' }
http://guides.rubyonrails.org/action_controller_overview.html#the-request-object
I would prefer altering the actual request if possible.
I got a feature spec which receives a token from an external site and redirects back to my app. Upon redirecting, the client is served an orders#new view, which calls #current_user.items. This call works in all environments except test. Capybara / Poltergeist or Selenium get undefined method items for nil class
I've tried:
assigning #current_user in before(:each)
changing the web driver
setting #current_user to User.first for Rails.env.test?in my
controller
How can I make sure #current_user works in test?
You need to either login in your feature spec, or use wardens test mode through devise to fake the login - see https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara - so that the current user is set (along with the relevant session cookies). Feature tests and the app each run separately when using any Capybara driver other than rack-test so attempting to set #current_user in a before(:each) isn't going to work. Setting it in the controller would probably work for that controller action but won't set the session cookies if you're following links, etc - and doing something like that in a controller just for testing is generally a bad idea.
Tom's answer made me realize that cookies are not stored because Capybara hosts are not static. I ended up explicitly setting the server_port, server_host and app_host in the rails_helper which worked like a charm:
Capybara.app_host = 'http://localhost:3000'
Capybara.server_host = 'localhost'
Capybara.server_port = '3000'
My app is not using Devise and Warden. Otherwise, Tom's answer would've worked.
I have a Passenger Rails 4 app running in a subdirectory: rails4.test/tools/ (rails4.test/ serves up unrelated content).
It works fine when testing manually but I am having trouble getting Capybara and Selenium to place nice.
In spec_helper.rb if I set the app host as such:
config.app_host = 'http://rails4.test/tools'
My Selenium tests run fine (i.e. the ones with "js: true"), however I receive routing errors for the ones that do not rely on Selenium. For example, if I have test code that says:
visit login_path
I'll get the following error:
No route matches [GET] "/tools/login"
However, if I set the app host as such:
config.app_host = 'http://rails4.test'
I get the opposite problem - the non-selenium tests work fine, but the Selenium tests visit the wrong url (http://rails4.test/controller/action instead of http://rails4.test/tools/controller/action) thus giving 404 errors. I assume the non-selenium tests work okay because the passenger config is routing the requests correctly?
I there an easy way to remedy this? I also attempted to add the following line to my test.rb file:
config.action_controller.relative_url_root = '/new_tools'
but it did not change anything, regardless of how config.app_host was set.
Is there a way to set the Selenium host separately? Am I doing something stupid?
Any suggestions would be appreciated! Thanks.
Problem solved by detecting if the :js => true option was set before each test and adjusting the app_host accordingly:
In spec_helper.rb in the RSpec.configure do |config| block:
# adjust app_host depending of if if js == true
config.before(:each) do
if(example.options[:js])
Capybara.app_host = 'http://rails4.test/tools'
else
Capybara.app_host = 'http://rails4.test'
end
end
I'm getting http://www.example.com whenever I use root_url in my tests.
It works fine in development, where I have this in config/environments/development.rb:
Rails.application.routes.default_url_options[:host]= 'localhost:3000'
Adding this doesn't work in config/environments/test.rb, though. What should I add to use localhost:3000 as the host in the test environment?
Testing code that depends on default_url_options causes all kinds of problems, see this thread and this issue for examples.
I've solved the problem by patching ActionDispatch::Routing::RouteSet in tests to force rails to include defaults for whatever options I want (in my case locale). See my answer in the github issue linked to above for details.
To override the host option using the same approach:
class ActionDispatch::Routing::RouteSet
def url_for_with_host_fix(options)
url_for_without_host_fix(options.merge(:host => 'localhost:3000'))
end
alias_method_chain :url_for, :host_fix
end
Put this in a file in support, should do the trick.
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?
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.
#request.host = 'user.myapp.com'
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
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
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.
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
#request.host = 'user.myapp.com' is not right.
should use host!('user.myapp.com')
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
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" }
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.