How to test for API errors? - ruby-on-rails

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.

Related

VCR Sharing requests between cassettes?

My VCR config is:
VCR.configure do |c|
c.configure_rspec_metadata!
c.cassette_library_dir = 'spec/cassettes'
c.hook_into :webmock
c.ignore_localhost = true
end
And a test example is:
it "creates a build", :vcr => {:cassette_name => "build/feature/create"} do
visit new_build_path(build)
fill_in("build_name", :with => "Test Build")
click_button("Create Build")
build = Build.first
page.should have_content("Build was successfully created.")
current_path.should == build_path(hub)
end
When running this test it calls out to several 3rd Party API's which requests get recorded via VCR. The problem I am having is that it appears that VCR is using requests from other cassettes while running which is causing intermittent failures for certain tests. I have checked the cassettes and sometimes (depending on order is how it appears) all requests will be recorded and played back perfectly. It is worth noting this is when the whole suite is run, they always work when run by themselves. I do not share cassettes between the specs that fail, the only thing that is shared is some common requests to the API's and I force the naming of the cassettes just to make sure it is using the correct ones. I hope this makes sense...
My main question is what can be causing this problem? When using record => :new_episodes the tests work perfectly as well but not when using the record => :once mode. This may be okay given the situation but I want to make sure I am not creating unnecessary requests and from my understanding record => :once should work given the requests for each spec should be isolated.
I know this may be hard to answer without more information so let me know if any will help. Thanks in advance!
As you said, it's hard to answer from the little bit of information you've given. I can give some suggestions for helping to troubleshoot, though.
You mention that you're using a capybara JS driver. Capybara's JS drivers are asynchronous, which means that you test thread may continue on before your app has processed a particular request -- so if the test script continues on and ejects or inserts a new VCR cassette, and then your app makes an HTTP request -- it could lead to race conditions.
VCR has a debug_logger option that will give you lots of insight into exactly what VCR is doing. It may answer your question.
Good luck!

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

Use fakeweb or webmock to simulate RestClient::GatewayTimeout in Ruby?

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.

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