Testing RESTful services can be a difficult task that goes beyond just testing pieces of code. A service is in fact composed of different components, and in order to test its functionality, the interactions between these components need to be tested.
Most of the time, when testing techniques are introduced, we learn how to test if a given method actually returns the expected results for a given input or set of inputs.
Testing services requires instead that we test how our service interacts with Amazon AWS, or with external APIs, or with our own load balancer. This includes testing testing how all other services interacting with our APIs react to certain error codes or specific responses.
We learned in Chapter 2 that the Rails framework was built to allow test–driven development (TDD) from the beginning. Rails in fact creates a test folder as soon as a new Rails project is created. If you list the contents of this folder you will find that a different subfolder is created for each component of the application, so that tests can be structured accordingly:
$
ls
test
controllers
fixtures
helpers
integration
mailers
models
test_helper
.
rb
Testing models in a Rails app usually means unit testing. With unit testing the smallest testable parts of your Rails application—the models, in this case—are tested individually and independently, to ensure that the defined methods work as expected.
Functional testing tests the controllers. The controllers handle the incoming requests and pass them to the various components of your service. When you are testing controller logic you are in fact testing the following:
Let’s go back to our LocalPic application from the previous chapter. Imagine that we have a simple controller with an action that just returns the status of our service—i.e., if we request a certain resource and get a 200 response, we know the service is up and running.
This controller creates a status endpoint that can be used by monitoring services to periodically check on our LocalPic API. If our API goes offline for any reason, the monitoring service will send a notification.
Let’s generate a simple status controller:
$
rails
g
controller
api
/
status
And then the actual code:
class
API
::
StatusController
<
ApplicationController
def
index
render
json
:
{
alive
:
true
}
end
end
And the test for it:
require
'test_helper'
class
API
::
StatusControllerTest
<
ActionController
:
:TestCase
test
'status should be fine'
do
get
:index
assert_response
:success
assert_equal
'{"alive":true}'
,
@response
.
body
end
end
Of course this is a simple example, but the logic applied to test controllers follows the same pattern: if we request a certain resource, do we receive the expected response?
We’ll continue by testing the actions on the pictures controller.
To test the index action we simply need to add the following test to the pictures_controller_test.rb file in test/controllers:
test
"should get index"
do
get
:index
assert_response
:success
end
Please note that this will only assert that the index action returns success. It will not assert what data the action actually produces.
Now we continue with the show action:
test
"should get show"
do
get
:show
,
id
:
1
assert_response
:success
end
Note that in this action we are passing the id
parameter. We therefore need to add it to our pictures.yml fixtures file.
We edit that file as follows:
one
:
id
:
1
title
:
"Test title"
Tests for the other controller actions can be found in the repository for the LocalPic application. I encourage you to try to write the tests yourself before looking at the repo for the solution. Writing tests is a great exercise to understand how our applications really work and to make sure that our code remains stable even when we change things.
When you start testing RoR applications, and especially their interactions with different services, you will start hearing about “stubbing” and “mocking” certain things. But what exactly are mocks, and what are stubs? The difference is very subtle and often confusing.
In 2007, Martin Fowler from ThoughtWorks published a famous blog post called “Mocks Aren’t Stubs”.
A mock object is an unreal object that we create for testing purposes. We use this when we do not want to or cannot use real data in the test.
Mocks verify behavior. The mock object is told what to expect during the setup phase of the test. During the verification phase of the test, we ask the mock to verify itself.
You’ll soon discover there are different terms to describe things that seem to overlap, at least in part. You’ll hear about test doubles, dummies, fakes, stubs, and mocks.
Let’s start from the beginning:
Mocks are not like fixtures. Fixtures are only fake database records.
Testing RESTful services means integration testing. A service must interact with the outside environment through the APIs it exposes and the external APIs it integrates.
Now, in a perfect world, all services you decide to integrate—whether internal to your organization or external—would always expose the same APIs, or at least notify their users if they were about to change something with regard to what data they return or how to make certain requests.
In the real world, this is hardly the case.
Integration tests have to take this very important aspect into consideration. You’ve probably designed some portions of your application in a way that integrates perfectly with one or more services that you have chosen to or had to integrate. What happens if one day these services change, and some of your app’s functionality does not work as expected?
A good idea to ensure that your integrations are stable is to test that each service is responding as expected. This means, in a continuous integration environment, that you write tests that are run every time your production environment is tested. This way you will be able to spot changes right away, hopefully before your users notice.
Imagine that you are integrating a payment service. You can write tests to ensure that you are always sending requests to the service in the right format. If something in the APIs changes, ideally your tests will notice it and raise an error or simply fail.
There are also tools that take care of things like this for you. These tools work like a pager for the APIs you want to monitor, and when something changes they send you a verification message.
In this chapter we took a quick look at testing, stubbing, and mocking RESTful services and APIs. We saw how we can test interactions between our app and the external services that will interact with our models and controllers. In the next chapter we will start talking about SOA practices applied to microservices and microapplications.
52.15.47.218