I'm writing tests for my Rails app. My app communicates with an external API that processes customer payments, specifically BrainTree. Now, I want to make sure my app's class that communicates with BrainTree works properly e.g. that it submits user information and other parameters to BrainTree correctly. Making the goal to only test that BrainTree and my App are communicating properly.
One thing to note, is that BrainTree has a sandbox account. To test my class, should I:
Write a feature test using something like Capybara and Rspec and test it from a user's perspective e.g. user logs in, fills out form, submits payment etc.
Write a request spec that just submits the required information and examines the return values. This is what I would prefer but is tricky since BrainTree requires js, and I am not sure I can do have js in a request spec without monkey patching Rspec, which I'd rather not do since I am still fairly new to Rspec and testing in general.
Write both feature and request specs
Write a completely different type of test
I have a feature test in place, but it seems cumbersome to use to just test an external API since it needs to open a browser, fill out forms, etc. in my feature spec I'd rather stub the external API and test the API as a unit test. A request spec seems more efficient but the js requirement seems like a roadblock.
Is there a Best Practice to what I should do in my scenario above?
In general, you don't typically want to write tests only for an external service, but instead for your own code that tests against the responses you receive.
The best way I've found to stub a response from an external API is the VCR gem. This will get a legitimate response and save it for use in future runs. You can erase the stored response occasionally to ensure continued functionality.
Another approach to testing this is to use a fake service that mimics the API. Fake Braintree Gem provides such functionality and I've used this with a mix of VCR for other tests to ensure correct functionality. There's many other approaches but you can test to see which one fits your needs
Related
The application is using Minitest on Rails 4 with Capybara.
I'd like to write an integration or feature test that stubs a websocket connection (the application uses Faye as a client) to return a specific message (like I'm used to doing with Webmock).
Is this possible? If so, can you provide an example? My research has not turned up any examples.
Your research hasn't shown up any examples because it's not really what you're supposed to be doing in feature tests. Feature tests are supposed to be end-to-end black box tests where you configure the data as required for the app to generate the desired results and then all interaction is done via the browser (no mocking/stubbing which technically alter your apps code). Additionally when connections between the browser and a 3rd party service are involved there is nowhere in your app where you can mock it.
It may be possible to stub a websocket connection from the browser with a programmable proxy like puffing-billy, however it's generally cleaner to produce a small fake version of the 3rd party service for testing purposes (sinatra app, etc) and point the browser at that rather than the original service when you need to craft custom responses. Additionally, there are a lot of fakes already out there, depending on what service you are using (fake-stripe, fake-s3, etc), which may provide the functionality you're looking for.
I have a rails app that interacts with an external api (Salesforce) that relies upon external data sitting in a remote database. I've written a wrapper that wraps this code so that users can just call get_by_id(id) instead of writing the corresponding sql query.
I want to test this code, and I am not sure how I should go about it. Should I be hitting the Salesforce backend database for the tests, calling the real methods? Or should I just mock the results of the method calls? I am perpetually confused by what I should test...
You should write like a suite for Salesforce's interaction.
A basic principle of testing, is that your test should not fail because of external factors. However, your app should be able to recover from SalesForce's errors.
From Rails 4 Test Prescriptions
Unfortunately, interacting with a third-party web service introduces a
lot of complexity to our testing. Connecting to a web service is
slow—even slower than the database connections we’ve already tried to
avoid. Plus, connection to a web service requires an Internet
connection... Some external services are public—we don’t want to post an update to Twitter every time we run our tests, let alone post a credit-card payment to PayPal.
Also, the book has some guidelines,
A fake server, which intercepts HTTP requests during a test and
returns a canned response object. We’ll be using the VCR gem ...* An
adapter, which is an object that sits between the client and the
server to mediate access between them.
A smoke test, which goes from the client all the way to the real server...a full end-to-end test of the entire interaction. We don’t
want to do this often, for all the reasons listed earlier, but it’s
useful to be able to guard against changes in the server API.
An integration test, which goes from the client to the fake server.
This tests the entire end-to-end functionality of our application but
uses a stubbed response from the server.
A client unit test, which starts on the client and ends in the
adapter. The adapter’s responses are stubbed, meaning that the adapter
isn’t even making fake server calls. This allows us to unit-test our
client completely separate from the server API.
An adapter unit test, which starts in the adapter and ends in the
fake server. These tests are the final piece of the chain and allow us
to validate the behavior of the adapter separate from any client or
the actual server
By the way, I think the book is a must-have
We've got a suite of UI tests for our app written using KIF which I'd like to convert to use the new Xcode UI test framework.
The app is a client of a Rest AI whose responses we're currently mocking by using NSURLProtocol to serve predefined JSON files in response to the GETs, POSTs, PUTs, etc... All the tests are defined using the data in these files, so I want to continue using them. The same endpoints on the server return different data at different points in the tests, so I can't mock them up-front, I need to be able to call a method while the test is running to mock the server's next response.
Unfortunately, using NSURLProtocol inside an Xcode UI test doesn't affect the tested app, and I've only seen ways of sending data to the app via launch arguments or environment, such as in this answer. I need to mock them differently at different stages during my tests. How can I mock network requests from inside the UI test in a way that changes during the test? Or how can I communicate with the app being tested so I can get it to mock the requests itself?
We've developed an embedded HTTP server in pure Swift, you can run the HTTP server with simple mock response handler in your test case, then pass the URL to the target app via environment variable like API_BASE_URL. I wrote an article about how to do it
Embedded web server for iOS UI testing
Basically there are two libraries, one is Embassy, it's the simple async HTTP server. Another one is Ambassador, a simple web framework for mocking API endpoints.
We have been facing the exact same problem trying to migrate from KIF to UI Tests. To overcome the limitations of UI Tests with regards to stubbing and mocking we built a custom link between the app and the test code by using a web server that is instantiate on the app. The test code sends HTTP requests to the app that get conveniently translated to a stubbing request (OHHTPStub), a NSUserDefault update, upload/download an item to/from the app. It's even possible to start monitoring network calls when you need to check that specific end points get called. It should be fairly simple to add new functionalities should you feel there's something missing.
Using the library is extremely simple, check it out on our github repo
You could either mock them using OHHTTPStubs, or by writing your own stubs.
In a nutshell, you should stub requests with a NSURLSession subclass and inject the stubbed NSURLSession into your networking layer.
The launchEnvironment property might be useful to pass mocked urls to the test.
My app interfaces with Google Maps for geocoding and Stripe for payments. Using VCR I've mocked all requests to these services, which works great. But the libraries for both are still being loaded in javascript_include_tags. What's the best way to deal with this so that integration tests can run completely disconnected from the internet?
If you want to stub the javascript, then why record the interactions in the first place? Am I missing something?
I'm pretty sure geocoding from a test is a violation of the google maps ToS. I think what you want to do is stub the geocoding itself.
Have never used Stripe, but I might take this approach: add a method in a spec helper to cache the Stripe javascript locally, i.e., when a connection is available, download the Stripe javascript. Then in your view load the javascript locally if Rails.env.test?.
For anyone looking for a solution these days, there is a nice gem called Puffing Billy.
Similarly to how VCR allows you to record and replay external requests made by your server code, this library allows you to do the same for external requests made by your client code.
I'm using Braintree Transparent Redirect to take payment info and create subscriptions that are stored with Braintree. What are best practices for testing the controller that interacts with Braintree? It seems like a lot of trouble to create a new customer and subscription in the Braintree sandbox every time I run my test. I'm using Rspec if that matters at all.
If you're still interested in using Rspec, check out the braintree ruby examples on github. All the tests are rspec, and there are lots of examples you can use.
https://github.com/braintree/braintree_ruby_examples
Hey try this out, in the process of setting up BT myself right now, planning to do testing this way roughly: http://www.enlightsolutions.com/articles/integration-testing-braintrees-transparent-redirect-with-rails-and-cucumber/
You can use fake_braintree to speed up your tests.
"This library is a way to test Braintree code without hitting Braintree's servers. It uses Capybara::Server to intercept all of the calls from Braintree's Ruby library and returns XML that the Braintree library can parse. The whole point is not to hit the Braintree API."
Or, if it doesn't do all you need, you can use Capybara with either the default java script driver or the capybara-webkit gem to test the transparent redirects.