How do I make webmock block/stub external requests from other gems? - ruby-on-rails

I'm trying to use webmocks to test part of my app that interacts with an external service. However, all of the API calls are actually happening within another gem. So it continues to make the requests, even though I've enabled webmock.
This properly is blocked and requires a mock:
it 'test webmock' do
Net::HTTP.get("www.google.com", "/")
end
But the test which contains API requests against the gem, which should be calling the external service is not.
Any idea how to make that test also require a mock?

Related

Rspec receive post from tested application

I am testing a running application using rspec/capybara. I have a route I want to test that is supposed to talk to a secondary service via a provided url.
Since the tests don't encapsulate the application, they just talk to it, I cant use the normal methods of stubbing out api calls, to make sure its calling the service properly.
What I would like is to be able to give the route a url, then have rspec receive a post back from the application. Is there a way to do this?
To be clear, I do NOT want rspec to mock/stub the request, because this isn't running as a wrapper to the application.
I will suppose the secondary service response is exposed somehow back to you.
So hitting https://not-my-service.com?secondary-service=http://service-i-control.com results in something that contains the response (partial or complete) from http://service-i-control.com.
If this service is up & running in production your secondary-service must also be something exposed to the internet, you can consider using something like ngrok to expose a local Rack application your testing environment is spinning up that returns a specific response.
If you don't mind using external services you could also consider using httpbin.org for example: https://not-my-service.com?secondary-service=https://httpbin.org/ip you will return a 200 OK with the IP of the origin that hit the server. So you could match that IP to https://not-my-service.com.
If you don't get any information besides the fact that it calls the secondary-service then I would suggest as part of the spec:
Spin up a rack application and expose it to the internet.
Hit the service passing your local application as parameter.
Wait until you get the request your are expecting, then stop the application and the test has succeeded.
Or it times out (say 30 seconds) and your test has failed (service was never called).

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 to test a Rails HTTP request to a Sinatra app?

Consider a Rails app that hits a (Sinatra app) API being developed separately from the Rails app. I want to test an API call from within the Rails tests.
The API code:
post '/foo/create' do
...
I created a mock, but that doesn't make sense because it is just a copy of the API file. That stinks.
It is possible to require the API file in the test. But how to call it from RSpec? There is no route in the Rails app for it.
One option is to start the API and make the HTTP call from the Rails test, but this is smelly because:
You have to start the API server to run the Rails tests
Why should a Rails test make a HTTP request? Rack::Test simulates this.
I don't think this will work because the apps have different test databases, but share the same production database.
EDIT: The point of the test is that the API call creates records that the Rails app is expecting. So the Rails app needs to test the state of the database after the API call is made.
Well. The perfect answer for you is a gem to mock the answer like webmock. It will fake a response when acessing that url, so on the test your app will make the requisition as it was for real, only that before it hits the web, it will hit your mock and respond with the desired answer.

Ruby on Rails: Best way to test a failed call to a third party API

I call a third party web service right now as part of my application. I am using the RestClient gem in order to do this. There are a ton of tools available to do the same thing, so that should not matter.
What I'm curious about is having good enough tests, nothing too fancy, where I can simulate how my application responds when the third party web service is unavailable for whatever reason. Be it that I exceeded a rate limit or a timeout due to network latency/complications, I just want to be able to take something like an HTTP status code and test what my application does in that event.
What's the best way to do this with Test::Unit? Right now the call to the third party service is encapsulated inside of one of my controllers. I have a simple module with some wrapper methods for the different end points of the remote service. I just want to make sure that my application does the right things when the service is or isn't available.
Is using an additional framework next to Test::Unit that can 'stub' the right way to go about doing this? Obviously I can't force a network timeout and starting to hack with things like IPtables just for tests is not worth the time. I'm sure this problem has been solved a million times as integrating things such as Facebook and Twitter into web applications is so popular these days. How do you test for failure when reaching those APIs in a robust/controlled format?
I would recommend using something like webmock to mock all of your http requests (not just to mock a failed request); it'll greatly speed up your test suite instead of having to actually hit the third party service every time you run the tests.
Webmock supports Rest Client and Test::Unit. Just put this code in your test/test_helper.rb file:
require 'webmock/test_unit'
As an example, to test a network timeout:
stub_request(:any, 'www.example.net').to_timeout
RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout
railscast: 291 (subscriber only) talks about testing with VCR and rspec (i know, not it's not Test:Unit)
anyway you could look into using VCR for this sort of thing

Integration tests of web services and XHR

I am using Steak and Capybara to do my integration tests. I also would like to test the serialization output (webservices using XML and JSON format) of my models. The problem is that JSON is only available by using a XML HTTP Request. So Capybara's visit method does not work. It also seems that I don't have access to xhr method in my acceptance tests (not sure why, because I require the normal spec_helper.rb in my acceptance_helper.rb).
How do you test web services? Using the xhr method (after requiring it somehow)? A special method with Capybara? Something else?
I also have some custom serialization (beside the normal my_object.to_xml). I guess it is better to do that in the model tests. Would you then still test the web service output of those custom serializations?
You can always create a "proxy" controller that makes requests to the web service and prints the result. It should be available only in test environment, obviously. Then your Capybara test visits the proxy and tests page content.

Resources