What is the `__identify__` route that Capybara looks for? - ruby-on-rails

When I disable Webmock or VCR I get the following error in my Rspec tests. Apparently it's looking for an /__identify__ route.
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: GET http://127.0.0.1:51768/__identify__ with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}
A bit of googling shows that this is something specific to Capybara, but I wasn't able to find out what exactly it's trying to do and why it might need to be stubbed.
Thanks!

You shouldn't be stubbing it - you should be allowing it (like all requests to your app). Its a route added via middleware Capybara installs when it starts up the "server" thread running your app. Capybara needs it to know the app has started up and is ready to go.

For VCR you can configure this way:
VCR.configure do |config|
// more VCR configs here
config.ignore_request do |request|
URI(request.uri).host == "127.0.0.1"
end
end

Related

Mocking login for remote services - Cucumber, Capybara, Rails

I have a Rails application that has changed from user authentication via devise to accessing a remote API for authentication. For the test suite, this requires that I mock the https request for authentication and return a valid response. We are using Cucumber and Capybara for testing and I am attempting to use the webmock Gem to mock the login response.
The login is initiated by a button click on a form (Capybara action 'click_button').
Unfortunately, although webmock appears to be installed correctly (I am able to make a Net::HTTP POST request and this is recognized by Webmock), the POST to the remote authorization facility is not being captured by webmock. I know that the form POST is making its way to the controller, because the stacktrace shows that the POST is being executed in the controller as it should and the error message is "Errno::ECONNREFUSED at ... Failed to open TCP connection".
I have tried:
WebMock.stub_request(:any, '127.0.0.1').to_return(body: {STATUSCODE: 1}.to_json)
and
WebMock::stub_request(:any, '127.0.0.1').to_return(body: {STATUSCODE: 1}.to_json)
and
stub_request(:any, '127.0.0.1').to_return(body: {STATUSCODE: 1}.to_json)
I have tried putting the stub_request call in the devise_steps.rb file, just before the "click_button" command, as well as in the features/support/env.rb and features/support/webmock.rb file.
I assume that what I am doing is so common that it has to be possible, but I have found nothing that indicates why the stub_request is not successful.
So the domain of the remote API for authentication is localhost? So it would be running on the same server with a different port? Then you have to mock the address with the port.
For instance your Rails app is running on port 80 and your auth API is running on 8080 then you have to do this.
stub_request(:any, '127.0.0.1:8080').to_return(body: {STATUSCODE: 1}.to_json)
I think Webmock should support different ports but I'm not 100% sure. Also have you set WebMock.disable_net_connect!(allow_localhost: true)?
https://github.com/bblimke/webmock#external-requests-can-be-disabled-while-allowing-localhost
Edit:
If this is not working, I suggest you make the API URL configurable and e.g. set it in tests to auth-api.com and then mock this.
The root cause of the problem was that the path on the stub_request was not complete. I had misread the webmock documentation....

How to test an HTTPS request in rspec?

Stackoverflow has a number of answers for this, but they all don't work. It seems like they're outdated.
I've tried code like
get :index, protocol: :https
What your code snippet suggests it's a spec type: :request. The thing is - HTTP is not really involved in those kinds of tests.
What happens is, when you type get :index, a request object is being made, passed to rails, and response object is captured and you test against its state
expect(response).to be_success
So you'd need to specify what exactly do you want to test:
is your app redirecting to https:// if someone uses http://
are your certificates correctly set up and your production app works with https:// protocol? (hint: this can't be testes in the spec tests really)
something else?
SSL encryption should be transparent to your web app, are you having problems with that? Is your production app failing with HTTPS, and works properly with HTTP?
(Feel free to compose a new question when you give it some thought)
Three ways to simulate HTTPS in request specs, with caveats:
If you're trying to simulate the behavior of your non-HTTPS app behind an HTTPS proxy, the easiest thing is to pass an HTTP header such as X-Forwarded-Proto:
get :index, headers: { 'X-Forwarded-Proto' => 'https' }
This will cause request.base_url to begin with 'https', request.scheme to return 'https', and request.ssl? to return true.
However, under this approach you may still have issues with Secure cookies in request specs as Rack::Test::Session and ActionDispatch::Integration::Session will still use http URLs internally.
A workaround for that is is to instead set env['rack.url_scheme'] to https:
get :index, env: { 'rack.url_scheme' => 'https' }
This will cause ActionDispatch::Integration::Session#build_full_uri to translate request paths to https URIs (while still not making any actual HTTP/HTTPS calls).
A potential issue here, however, is that it only affects this specific request; in a complex setup with OmniAuth or other gems you may still end up with a mix of HTTP and HTTPS requests and more cookie issues.
What I finally settled on, myself, is to brute-force configure all integration-test requests to HTTPS. In rails_helper.rb:
config.before(:example, type: :request) do
integration_session.https!
end
This works for my tests, even with OmniAuth cookies in the mix, and it supports hard-coding secure: true and cookies_same_site_protection = :none in application.rb. It may have other negative side effects, though, that I just haven't run into.

How to mock requests made by the browser in a Rails app?

WebMock works fine for requests made by the app. But how to mock AJAX requests made by the browser to 3rd party services?
Rails 4.
Finally found 2 answers:
Approach #1: Puffing Billy
A rewriting web proxy for testing interactions between your browser and external sites.
Works like WebMock:
proxy.stub('http://www.google.com/')
.and_return(:text => "I'm not Google!")
Approach #2: Rack Middleware
Although the previous approach can work, I discovered that it does not work with WebMock. So, if you want to mock your browser requests to external services, you can't mock your app requests.
The approach that worked for me is to run a separate Rack app and inject it into the middleware stack:
spec_helper.rb:
def test_app
Rack::Builder.new{
use ExternalServiceMock
run app
}.to_app
end
Capybara.app = test_app
ExternalServiceMock is a rack app that only responds to certain request paths.
For this particular app, all of the external service URI's were stored in configs, and I set them in the spec helper:
ENV[ 'EXTERNAL_SERVICE_URI' ] = 'http://localhost:3000'
This forces all external requests to be sent to the ExternalServiceMock.
So basically you only need to save the response from 3rd party services and stub the request, then you can test it! Also checkout VCR: https://github.com/vcr/vcr.

How can I block external connections with RSpec & Capybara?

With my Rails project, I would like to write tests non-ideal conditions such as lack of internet connection or timeouts. For example, I am using a gem to contact an API and would like to make sure that I handle the error correctly if there is a connection issue between my app and the external API.
I can do this already by making a fixture with VCR and removing the response from the "cassette". However, this has drawbacks:
It has to be done manually.
The cassettes can not be gitignored if I am working with a team(which I am).
How can I simply create a block in my RSpec tests that will prevent external connections, simulating the lack of an internet connection?
Per this thought-bot article
https://robots.thoughtbot.com/how-to-stub-external-services-in-tests#disable-all-remote-connections
# spec/spec_helper.rb
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
I've never tried to do this, but perhaps you could use webmock to stub out all requests.
before do
stub_request(:any, /.*/).to_return(body: "errors", status: 422)
end
More info on stubbing external services.

How to test for API errors?

My rails app talks to a couple different APIs, and I'd like to write tests to make sure that errors are being handled properly in case there is a problem with a request. For example, if the service is down and not reachable.
Is there a way I can simulate an error coming back from a service even though it might be fine during the test?
The webmock gem helps you with this. For your specific question scroll down to "Response with custom status message".
stub_request(:any, "www.example.com").to_return(:status => [500, "Internal Server Error"])
req = Net::HTTP::Get.new("/")
Net::HTTP.start("www.example.com") { |http| http.request(req) }.message # ===> "Internal Server Error"
I've used this gem with Rspec and Test::Unit. The biggest drawback is that the matching logic which decides what requests apply to a particular mock doesn't always work as you'd expect. It's a minor gripe though. The gem is a net-positive, and much better than trying to hit a live service from a test case.
You can use a mock instead of the real service, and make it give back an error.

Resources