Use fakeweb or webmock to simulate RestClient::GatewayTimeout in Ruby? - ruby-on-rails

I often see RestClient::GatewayTimeout in my application. I'm trying to figure out how to properly test for this, to make sure my application handles it gracefully.
The closest thing to this that I see is stub_request(:any, 'www.example.net').to_timeout
That raises RestClient::RequestTimeout however and not RestClient::GatewayTimeout. What's the best way to simulate the latter?

stub_request(:any, 'www.example.net').to_raise(RestClient::GatewayTimeout)
This will obviously work only for RestClient and if you change RestClient to
some other library, you'll have to change your test too.

Related

How can I use VCR with Rails 5.1 system tests?

Many things on the web seem to suggest that VCR can be used with Capybara.
I have three problems.
This doesn't make much sense to me, because the test driver and the application code don't share memory.
I'm not finding full recipes on how to set this up.
I'm finding bits and pieces of how people have set this up, but it's outside of the context of rails 5.1, which does the capybara setup behind the scenes.
How do I configure a Rails 5.1 app, Capybara, and VCR to work together for system tests?
(My headless browser is phantomjs, driven by poltergeist. But I don't need to intercept requests from the browser, only server-side requests. If I needed to intercept from the browser I would probably use a full http proxy server, like puffing-billy.)
I'm assuming you mean Rails 5.1 since Rails 5 doesn't have system tests.
The copy of the application Capybara runs for testing is run in a separate thread, not a separate process. This means they do have access to the same memory, and loaded classes
There is nothing special required for configuring WebMock or VCR beyond what their READMEs already provide
The setup of Capybara and how Rails handles it is irrelevant to the configuration of WebMock or VCR. Additionally, even when using Rails 5.1 system tests all of the normal Capybara configuration options are still usable.
That all being said, there are a couple of things to be aware of here. Firstly, WebMock/VCR can only deal with requests made by your app (not from the browser which you stated you don't need) and it's generally better to use faked services (if possible) rather than WebMock/VCR when doing end to end system tests since there is less interference with the code under test.
If this doesn't answer your issues, post a question with a specific issue you're having, the code that's causing your issue, and the error you're getting.

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.

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

How should I deal with online dependencies when running Rspec offline?

I would like to run RSpec to test my code both when I'm connected to the web and when I'm not.
Unfortunately there are some tests in the application which are dependent on having a live connection - email sending, Facebook integration etc.
Is there a best-practice way to create an online/offline testing environment or is this bad practice? Judging by how little I can find about this on the web I'm guessing the latter.
Normally in situations like that you would mock the parts of the code that connect outside your application. This allows you to test against the expected results from the service/system you are connecting to. It's also quicker to run tests.
There's a brief tutorial on mocking with rspec here but I'm sure you can find plenty yourself.
For testing that emails get sent there are other approaches if you are sending through ActionMailer. There's a section on that in the rails testing guide.
EDIT (in response to comment):
You could put a method in TestHelper to only run tests when you are online. Something like:
def when_online
if test_remote_connectivity
yield
else
puts "Skipping test offline."
end
end
Then you can call it like:
def test_facebook
when_online do
.....
end
end
Not sure I entirely advocate it but it might do what you want!
You could use webmock inside the tests/specs you don't want connecting to the remote resource.

Mock out Curl::Easy.perform? (in Curb)

Is there a way to mock out Curb's Easy.perform method for unit testing? I use this to hit Facebook's graph API, and none of the http mock libs seem to support Curb.
What's the best approach here?
WebMock has recently added Curb support. :)
http://github.com/bblimke/webmock
I really think fakeWeb is a great way to fake out network calls; Whatever HTTP library you use, you'll just specify what response text and code you want to receive.
Described as:
FakeWeb is a helper for faking web
requests in Ruby. It works at a global
level, without modifying code or
writing extensive stubs
Example from the github repository:
FakeWeb.register_uri(:get, "http://example.com/test1", :string => "Hello World!")
Net::HTTP.get(URI.parse("http://example.com/test1"))
=> "Hello World!"
Net::HTTP.get(URI.parse("http://example.com/test2"))
=> FakeWeb is bypassed and the response from a real request is returned

Resources