Creating mock RESTful APIs using Sandbox
This post was published on October 30, 2015While browsing through my Twitter feed a couple of days ago I saw someone mentioning Sandbox, a Software as a Service (SaaS) solution for quick creation and deployment of mock services for development and testing purposes. After starting to play around with it a bit, I was rather impressed by the ease with which one can create useful mocks from Swagger, RAML and WSDL API specifications.
As an example, I created a RAML API model for a simple API that shows information about test tools. Consumers of this API can create, read, update and delete entries in the list. The API contains six different operations:
- Add a test tool to the list
- Delete a test tool from the list
- Get information about a specific test tool from the list
- Update information for a specific test tool
- Retrieve the complete list of test tools
- Delete the complete list of test tools
You can view the complete RAML specification for this API here.
Creating a skeleton for the mock API in Sandbox is as easy as registering and then loading the RAML specification into Sandbox:
Sandbox then generates a fully functioning API skeleton based on the RAML:
Sandbox also creates a routing routine for every operation:
var testtools = require("./routes/testtools.js") /* Route definition styles: * * define(path, method, function) * soap(path, soapAction, function) * */ Sandbox.define("/testtools", "POST", testtools.postTesttools); Sandbox.define("/testtools", "GET", testtools.getTesttools); Sandbox.define("/testtools", "DELETE", testtools.deleteTesttools); Sandbox.define("/testtools/{toolname}", "GET", testtools.getTesttools2); Sandbox.define("/testtools/{toolname}", "PUT", testtools.putTesttools); Sandbox.define("/testtools/{toolname}", "DELETE", testtools.deleteTesttools2);
and empty responses for every operation (these are the responses for the first two operations):
/* * POST /testtools * * Parameters (body params accessible on req.body for JSON, req.xmlDoc for XML): * */ exports.postTesttools = function(req, res) { res.status(200); // set response body and send res.send(''); }; /* * GET /testtools * * Parameters (named path params accessible on req.params and query params on req.query): * * q(type: string) - query parameter - Search phrase to look for test tools */ exports.getTesttools = function(req, res) { res.status(200); // set response body and send res.type('json'); res.render('testtools_getTesttools'); };
Sandbox also creates template responses (these are rendered using res.render(‘testtools_getTesttools’) in the example above). These are mostly useful when dealing with either very large JSON responses or with XML responses, though, and as our example API has neither, we won’t use them here.
To show that the generated API skeleton is fully working, we can simply send a GET to the mock URL and verify that we get a response:
Now that we’ve seen our API in action, it’s time to implement the operations to have the mock return more meaningful responses. We also want to add some state to be able to store new entries to our test tool list for later reference. For example, to add a test tool submitted using a POST to our list – after verifying that all parameters have been assigned a value – we use the following implemenation for the export.postTesttools method:
/* * POST /testtools * */ exports.postTesttools = function(req, res) { if (req.body.name === undefined) { return res.json(400, { status: "error", details: "missing tool name" }); } if (req.body.description === undefined) { return res.json(400, { status: "error", details: "missing tool description" }); } if (req.body.url === undefined) { return res.json(400, { status: "error", details: "missing tool website" }); } if (req.body.opensource === undefined) { return res.json(400, { status: "error", details: "missing tool opensource indicator" }); } // add tool to list of tools state.tools.push(req.body); return res.json(200, { status: "ok", details: req.body.name + " successfully added to list" }); };
Likewise, I’ve added meaningful implementations for all other methods. The complete code for our mock API implementation can be found here.
Finally, to prove that the mock API works as desired, I wrote a couple of quick tests using REST Assured. Here’s a couple of them:
// base URL for our Sandbox testtools API static String baseUrl = "http://testtools.getsandbox.com/testtools"; // this is added to the URL to perform actions on a specific item in the list static String testtoolParameter = "/awesometool"; // original JSON body for adding a test tool to the list static String testtoolJson = "{\"name\":\"awesometool\",\"description\":\"This is an awesome test tool.\",\"url\":\"http://awesometool.com\",\"opensource\":\"true\"}"; // JSON body used to update an existing test tool static String updatedTesttoolJson = "{\"name\":\"awesometool\",\"description\":\"This is an awesome test tool.\",\"url\":\"http://awesometool.com\",\"opensource\":\"false\"}"; @BeforeMethod public void clearList() { // clear the list of test tools delete(baseUrl); // add an initial test tool to the list given(). contentType("application/json"). body(testtoolJson). when(). post(baseUrl). then(); } @Test public static void testGetAll() { // verify that a test tool is in the list of test tools given(). when(). get(baseUrl). then(). body("name", hasItem("awesometool")); } @Test public static void testDeleteAll() { // verify that the list is empty after a HTTP DELETE given(). when(). delete(baseUrl). then(). statusCode(200); given(). when(). get(baseUrl). then(). body(equalTo("[]")); }
An Eclipse project containing all of the tests I’ve written (there really aren’t that many, by the way, but I can think of a lot more) can be downloaded here.
One final note: Sandbox is a commercial SaaS solution, and therefore requires you to fork over some money if you want to use it in a serious manner. For demonstration and learning purposes, however, their free plan works fine.
Overall, I’ve found Sandbox to be a great platform for rapidly creating useful mock API implementation, especially when you want to simulate RESTful services. I’m not sure whether it works as well when you’re working with XML services, because it seems a little more cmplicated to construct meaningful responses without creating just a bunch of prepared semi-fixed responses. Having said that, I’m pretty impressed with what Sandbox does and I’ll surely play around with it some more in the future.
"