How to test a Rails HTTP request to a Sinatra app? - ruby-on-rails

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.

Related

Rails http request itself in tests hangs

Problem
Making an HTTP request from a model to a route on the same app results in request timeout.
Background
Why would you want to http request itself rather than call a method or something?
Here is my story: there is a rails app A (let's call it shop) and a rails app B (let' call it warehouse) that talk to each other over http.
I'd like to be able to run both of them in a single system test to test end-to-end workflow. Rails only runs a single service, but one can mount app B as a rails engine into the app A, effectively having two apps in a single service. However, they still talk to each other over http and that's the bit that does not work.
Thoughts
It looks as if the second request hits some kind of a thread lock around active record or something. The reason I thinking about active record, is that I was able to make an http call to itself from the controller (that is, before active record related code kicked in)
Question
Is it possible to work around that?

Running integration/acceptance tests on the frontend. Need an API for the frontend to tell Rails which database state to set up for each test

My frontend is an EmberJS-based app. It's totally async in nature, so testing it with Capybara is pain and misery. On the other hand, Ember provides a fantastic test suite out of the box which makes acceptance testing fun and effective.
Normally, either fixtures or backend mocks are used to acceptance-test Ember apps. But testing against mocks does not satisfy me at all:
It will not reveal possible API inconsistencies between the backend and the frontend, race conditions, etc.
It is impossible to test backend business logic this way. Such tests are not integration tests.
Finally, acceptance tests require persistency, so you have to replicate backend behavior in a mock. It's very tedious and you effectively end up implementing two backends.
So I want to test against the real backend! It's trivial to set up Ember to use a local backend instance for testing. But the problem is that the backend will persist its state between individual tests and even test sessions.
That's why I'm thinking of implementing a special public API in Rails:
The API is only available when Rails is run with a specific flag or an env var.
Rails runs in a non-testing mode, serving normal API calls as it would in production.
Before each test, the frontend calls the special API, telling Rails which database setup is needed for this specific test.
When a call to the special API is received, Rails cleans the database and populates it with the requested data. For example, to test item deletion from the cart, the database should have three items in the cart.
Rails finishes the API request, and the frontend starts the test.
Frontend runs test steps, using the normal backend API as it would in production: log in, create posts, comment on them. It will also try doing some forbidden things, e. g. edit posts while not logged in, exceed text length constraints, etc and verify whether the backend rejects forbidden actions.
When the frontend runs next test, it will call the special API again. Rails will discard the state produced by the previous test and set up a new one, for this specific test.
I'm a frontend dev with a sketchy knowledge of Rails. Factory Girl and Database Cleaner seem to be the right tools for the job, but there is absolutely no information how to use them outside Rails' normal test environment. I guess I need a controller or a Rails engine or something.
So the question is: how do I make an API in Rails that can be used by the frontend to tell Rails to set up a certain database state with a fixture factory, while Rails are running in a non-test mode i. e. serving REST API and not running through RSpec/Capybara/Cucumber?
Bonus feature: fixture factory properties should be defined on the frontend so that test code is stored in one place. Thus, the backend should be able to accept fixture factory properties via the special API. Defaults can still be defined in the backend's codebase.
I believe this could become an acceptance/integration testing best practice. If I manage to implement it, I promise to publish a gem.
May be something like this
config/routes.rb
namespace 'test_api' do
resource 'db_transaction', only: [:create, :destroy]
end if Rails.env.test?
controllers/test_api/db_transactions_controller.rb
require 'database_cleaner'
def create
DatabaseCleaner.start
end
def destroy
DatabaseCleaner.clean
end

How do I make webmock block/stub external requests from other gems?

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?

How to simulate external APIs?

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.

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