Skip to content

Testing RESTful APIs

Christopher Lane Hinson (Web Performance) edited this page Jun 13, 2016 · 3 revisions

Prerequisites

Before attempting this tutorial, make sure you've read and understood the contents in Getting Started.

Introduction

This tutorial corresponds to example #2 in the legion-starter-pack. In this tutorial, we will:

  1. Introduce a new app in legion-obstacle-course (our toy server for these tutorials) called "ticket."
  2. Use legion to make RESTful JSON calls to the toy server.

The RESTful Legion

Instead of using the raw legion-io-fetch API, we'll be using a tiny convenience-version of the API designed for RESTful JSON.

const rest = require('legion-io-fetch').rest;

Unlike the core fetch API, which is completely generic, this rest API specializes to the various RESTful HTTP methods (GET, POST, PUT, PATCH, DELETE) as individual javascript function calls. It also automatically stringifies and uploads your javascript objects as JSON content for all methods except GET.

The Ticket App

The ticket app (/ticket) is a part of legion-obstacle-course that lets us create and redeem tickets. By calling /ticket/new we get a JSON object with a new ticket number. By calling /ticket/redeem we can redeem the same ticket. Tickets can only be redeemed once. In real life, a ticket might be a session identifier, and redeeming the ticket might mean closing the session.

Validating Responses

In any load test we need to validate each response to make sure that it is consistent with our expectations. Otherwise, we can not trust that the test results represent a successful outcome. To this end, we start by writing a validation method:

function assertSuccess(response) {
  if( !response.ok )
    throw new Error('Response was not Ok.');

  if( response.json.status !== 'success' )
    throw new Error( response.json.status + ': ' + response.json.reason );

  return response;
}

We just assert that the status of a response is OK and the JSON payload also reports a success. If either of those conditions are not met, we throw an error with an informative method.

Note that assertSuccess() returns its own argument. This means we can pass it through chain() and still use the response for something else.

Defining the Testcase

Our testcase will be structured a lot like a chain of then()-able calls in a Promise-based API. We'll start by making a request and use the chain() method to add subsequent steps to follow after that request. Each method that we pass to chain() receives a single argument, which is the output of the previous step in the chain.

  .testcase(L.of()
    .chain(rest.post(host + '/ticket/new'))
    .chain(assertSuccess)
    .chain(response => rest.post(host + '/ticket/redeem?ticket='+ response.json.ticket))
    .chain(assertSuccess))

We call localhost/ticket/new to get a new ticket and validate the response. The response to this call will contain a JSON object with a ticket field containing the unique ID of our new ticket. Then we call localhost/ticket/redeem and use the ticket field of the previous request as a query parameter. If this fails for some reason, perhaps because our ticket number is invalid, then the final call to assertSuccess() will detect the error and throw an exception.

Running the Testcase

We use the main() method to wrap this testcase into an executable program. You can run it from the starter pack by calling:

node examples/002_ticket.js

If you want to run multiple simultaneous users, try this, which will run the testcase 10 times in parallel:

node examples/002_ticket.js -n 10

Clone this wiki locally