I am trying to test a "scan" method that I have. Within this scan method, I make an API call (among other things).
How can I test this method without triggering unneeded API calls?
One approach is to just stub out the call to the API:
allow(thing).to receive(:action).and_return(response)
Another approach is to allow the API call to go through, but to intercept it and return a mock response using VCR. To do this you "record" a request and "play it back".
VCR is handy when you need to handle the the entire response in the test subject. Just run the test against the real API one time, then subsequent tests can use the VCR "cassette". OTOH this is slower than simply stubbing the call, especially if you only need to mock the status and not the entire response.
TL:DR, stub if you can, but don't hesitate to use VCR when it saves you work.
You shouldn't be making API calls in your test environment. In order to prevent these calls, you should stub the method so that it return a true or success upon being called.
Related
I want to use Cypress as a testing tool with the cypress-on-rails plugin.
However during a cypress scenario I want to enable/wrap all rails backend requests with vcr so all requests are captured and replayed.
Typically you would tag a rspec or cucumber file that essentially wraps an entire block of code to perform this. The nature of cypress is that it's completely client/javascript driven and a scenario plays out with multiple ajax requests from the client.
The insert/eject methods on which the use_cassette method is built are publicly available.
https://www.rubydoc.info/gems/vcr/VCR#insert_cassette-instance_method
VCR.insert_cassette('my_cassette')
# do stuff
VCR.eject_cassette
I am exposing an API that responds asynchronously to certain requests. This is possible, as the client appends a callback_url in their request, to which the asynchronous action will send the result when it completes.
Problem is, the action completes while inside a model, which makes it tricky to keep a clear seperation of concerns, as I usually handle stiching together JSON responses in the controller using ActiveModelSerializer.
Any advice on how to approach this in an idiomatic way?
Thanks
My approach would be to extract the outgoing callback response into a separate service (called from within the model) and place that service on an asynchronous queue.
This service should be as generic as possible. Any logic that relates to building/sending/logging outgoing responses would then be contained within the service, and is separated out of the Model.
I would then wrap the service call in an asynchronous priority queue system, such as DelayedJob. This would allow the Model to do its thing before handing the response off to the service for asynchronous execution.
The benefit to using a queue system is that should anything prevent the response from being posted it will not 'freeze' the Model whilst executing. Bottom line; the Model can hand the response over to the queue and forget about the details of sending the response.
Ryan B. himself says (pro account required):
OlderRailsCast
I have a model called Video. This model has a after_save callback which runs a method #upload_video_to_depot. That method uses :file param (which is not saved in the db) and uploads a video file to a remote API using RestClient.
The question is - how to handle that in my specs without actually sending the file to the API ? I need to test my Video model and #upload_video_to_depot method but i can't imagine how it should be done (i'm quite fresh in the TDD thing).
Can it be fully handled in the specs or it also involves some changes in my model?
Use a gem like WebMock to stub external requests. You can set expectations on what requests were sent and with what params, as well as the responses the server should be giving you.
This will allow you to ensure that your REST client is sending the correct params to the correct place and processing responses correctly.
I have two applications, say, A and B, talking to each other via API, now I am writing cucumber tests for A, I have two options:
Just test if the API is sent to B and stub the response from B
Setup test data on B from A (since i am testing A), and send real request to B, and record the request/response with VCR
I prefer option #1, but my coworker says it needs at least one real request to make sure the system (including A and B) is working.
My concern is:
How to prepare testing data for B from A's tests?
It's fragile to mix them together, anything changed on B may cause failure on A
Any comments?
For the majority of your tests, stub the request/response, that way the test will pass when offline, or what not.
For one test, do a simple test that the external service is behaving as your stubs and mocks say it should.
E.G. Doing a get request still returns JSON with the attributes you expect to ensure your mocks are valid.
For the most part, "Up time" for an external service shouldn't be monitored by your test suite. Just that it behaves how you expect it to.
For the uptime concern you should look at the sysadmin side with Nagios, Pingdom, Pagerduty or what not.
You are writing cucumber test, means it is an integration test.
For integration test, you'd better not mock anything, it's the last safety guard to keep your application save.
So you'd better send at last once real request to make sure your request is correct and what's more you can repeat this real request at any time.
the problem of solution 1:
You can not make sure B changing API implement
You can not make sure A send correct parameter to B
It's hard to mock complex request
So I sugguest create a sandbox app for B, make real request
We call many different external APIs in our system and now I'm looking for a system I can use to simulate those APIs so we can test ours in the Staging and Development environments?
Our application is written in Ruby on Rails 3.0 but since all the API calls to and from it are over HTTP there is no language dependency.
VCR will record the actual input from the webservice and then replay that feedback from then on.
To simulate it completely, you can use fakeweb. You'll record output to a file and have it sent back to your application.
This something called test mocking/stubbing and is a common practice. Basically you override the response code of the API call to return data w/o actually doing the HTTP request. Just search it for more details.