Using Sinon stub to Replace External Service Calls in Tests

When writing tests for a service you’ll often find that there are other dependent services that may need to be called in some way or another. You won’t want to actually invoke the real service during test, but rather ‘stub’ out the dependent service call function. Sinon stub provides an easy and customisable way to replace these external service calls for your tests.

Practical Example: AWS SQS sendMessage Sinon Stub

Let’s say you have an AWS Lambda function that drops a message onto an SQS queue. To test this function handler, your test should invoke the handler and verify that the message was sent.

This simple case already involves an external service call – the SQS sendMessage action that will drop the message onto the queue.

Here is a simple NodeJS module that wraps the SQS sendMessage call.

// sqs.ts

import AWS = require("aws-sdk");
import { AWSError } from "aws-sdk";
import { SendMessageRequest, SendMessageResult } from "aws-sdk/clients/sqs";
import { PromiseResult } from "aws-sdk/lib/request";

const sqs = new AWS.SQS({apiVersion: '2012-11-05'});

export function sendMessage(messageBody: string, queueUrl: string) : Promise<PromiseResult<SendMessageResult, AWSError>> {

  var params = {
    QueueUrl: queueUrl,
    MessageBody: messageBody,
  } as SendMessageRequest;

  return sqs
    .sendMessage(params)
    .promise()
    .then(res => res)
    .catch((err) => { throw err; });
}

The actual Lambda Handler code that uses the sqs.ts module above looks like this:

// index.ts

import { sendMessage } from './sqs';
import { Context } from 'aws-lambda';

export const handler = async (event: any, context?: Context) => {

    try {
        const queueUrl = process.env.SQS_QUEUE_URL || "https://sqs.eu-west-2.amazonaws.com/0123456789012/test-stub-example";
        const sendMessageResult = await sendMessage(JSON.stringify({foo: "bar"}), queueUrl);
        return `Sent message with ID: ${sendMessageResult.MessageId}`;
    } catch (err) {
        console.log("Error", err);
        throw err;
    }
}

Next you’ll create a Sinon stub to ‘stub out’ the sendMessage function of this module (the actual code that the real AWS Lambda function would call).

Setup an empty test case that calls the Lambda handler function to test the result.

// handler.spec.ts

import * as chai from 'chai';
import * as sinon from "sinon";
import { assert } from "sinon";

import * as sqs from '../src/sqs';
import { handler } from '../src/index';
import sinonChai from "sinon-chai";
import { PromiseResult } from 'aws-sdk/lib/request';
import { SendMessageResult } from 'aws-sdk/clients/SQS';
import { Response } from 'aws-sdk';
import { AWSError } from 'aws-sdk';

const expect = chai.expect;
chai.use(sinonChai);

const event = {
  test: "test"
};

describe("lambda-example-sqs-handler", () => {
  describe("handler", () => {

    it("should send an sqs message and return the message ID", async () => {

      // WHEN

      process.env.SQS_QUEUE_URL = "https://sqs.eu-west-1.amazonaws.com/123456789012/test-queue";
      const result = await handler(event);
      
      // THEN

      expect(result).to.exist;
      expect(result).to.eql(`Sent message with ID: 123`);
    });
  });
});

Right now running this test will fail due to the test code trying to call the sqs.ts module’s code that in turn calls the real SQS service’s sendMessage.

Here is where Sinon stub will come in handy. You can replace this specific call that sqs.ts makes with a test stub.

In the describe handler section, add the following just before the ‘it‘ section.

const sendMessageStub = sinon.stub(sqs, "sendMessage");

let stubResponse : PromiseResult<SendMessageResult, AWSError> = {
  $response: new Response<SendMessageResult, AWSError>(),
  MD5OfMessageBody: '828bcef8763c1bc616e25a06be4b90ff',
  MessageId: '123',
};

sendMessageStub.resolves(stubResponse);

The code above calls sinon.stub() and passes in the sqs module object, as well as a string (“sendMessage” in this case) identifying the specific method in the module that should be stubbed.

An optional promise result can be passed in to resolves() to get the stub to return data for the test. In this case, we’re having it return an object that matches the real SQS sendMessage return result. Among other things, this contains a message ID which the Lambda function includes in it’s response.

Add a test to verify that the stub method call.

assert.calledOnce(sendMessageStub);

If you run the test again it should now pass. The stub replaces the real service call. Nice!

sinon stub test result

Conclusion

Replacing dependent service function calls with stubs can be helpful in many ways. For example:

  • Preventing wasteful, real service calls, which could result in unwanted test data, logs, costs, etc…
  • Faster test runs that don’t rely on network calls.
  • Exercising only the relevant code you’re interested in testing.

Sinon provides a testing framework agnostic set of tools such as test spies, stubs and mocks for JavaScript. In this case, you’ve seen how it can be leveraged to make testing interconnected AWS services a breeze with a Lambda function that calls SQS sendMessage to drop a message onto a queue.

Feel free to Download the source code for this post’s example.

Sneak peek: New game in development for iPhone / iPod Touch

So I thought I would do a post where I can post updates as I make progress on this new game I am developing. The platform is of course the iPhone / iPod touch. That is all versions from the first iPhones up to the new iPhone 4. I may even do an iPad version later. I have really been putting some effort into learning more about OpenGL and more specifically the cocos2d engine. By starting this game I have learnt tons about particle effects, sprites and game logic. I am even having to dust out the cobwebs in my brain when it comes to mathematics! The shooting and accelerometer movement algorithms in this game took some effort to get right! Note that all graphics and code are original and done myself (i.e. all game content is self-made!) My graphic design abilities have definitely helped with regard to the making of my ship, enemy, back drop and other game graphics. I’ll post further updates as I go along. The name is going to stay secret till its released though 🙂

This is a top-down, accelerometer controlled space shooter, with fluid 60fps animation and great effects. Anyway, here are a few screenshots of what I have so far. Note that any text you see is not final and just there for testing purposes for now. The enemy ship and player graphics are also going to be refined before I submit this.

Note the Battlestar reference in my last screenshot 😀 Anyway, if you have any ideas or questions, feel free to post them below.

An update on what I have been busy with lately…

Thought I would do a quick blog post on what I have been busy with lately.

1. My first iPhone / iPod Touch game (released on Cydia for Jailbroken devices)

So this is something I have been busy with over the last few months – coding bits and pieces here and there whenever I get a bit of spare time on my hands. Its nothing special – just a simple Maze game. You use your device’s accelerometer to navigate your character through 5 maze levels. If you touch the walls you lose health. The longer you take to complete the maze, the more score you lose too. So the point is to get through in the quickest possible time without touching walls. I learnt the basics of how to work with the accelerometer, game loops, twitter integration, a little bit of PHP and mySQL for the Highscore system and a fair amount of general objectiveC code. There are a couple of bugs in the game at the moment (like the way you get a little stuck on walls – issue with my game loop) that I don’t really have time to sort out at the moment. But hopefully I’ll get more time in the near future to figure out my mistakes and fix these. You can check the game out in more detail here or you can download it for Jailbroken Apple devices on Cydia. Search for “Speed Maze”.

2. Moving

Well we’re moving house in the next month or so. We have found a place a little closer in to London that is going to offer far more room, an awesome garden, park across the road, and to top it all off, its in a nice quiet cal-de-sac. As such, I have been taking the opportunity to eBay some surpluss hardware and gadgets I have had lying around for a while. This includes various PCs and bits of hardware I have had lying around, plus around 20U worth of Dell PowerEdge servers! (See image above).

3. Other

My home VMware lab – I have also been building various ESX and vCenter labs here at home to play with in a non-production environment. This is great as it allows me to test all kinds of crazy things I really don’t want to try out at work! I have chopped and changed the hardware, but finally have two different labs going. One is a nested set up of virtualized ESX 4.0 hosts, running under a main ESX 4.0 host if you see what I mean.  The other is running on a PowerEdge 6850 server at the moment – 4 x Xeon 3.16GHz CPUs and 4GB RAM. The issue I have here is that there is no Intel VT (hardware virtualization) support on these processors. So although ESX 4.0 runs OK, I can only run 32-bit VMs for now. Exchange 2010 and other 64-bit VMs will have to stay on my main gaming PC for now then. I also found this great WordPress plugin by lynxbat on Twitter. Once set up, it displays statistics from your VMware ESX host or vCenter Server. You can take a look at my current lab stats on the right in the sidebar. Get the plugin over here: WP-vSphereStats. Apart from that, we have also been planning one or two soon to be taken, well deserved holidays. We’ll be off to France soon, after which we’ll be taking a nice long holiday in South Africa. Excited to see friends and family again soon!