How To: Stub AWS Services in Lambda Functions using Serverless, Sinon.JS and Promises

I know the title is a quite a mouthful, but if you are trying to run tests on your Lambda functions that interact with AWS Services using the aws-sdk node module, then you’ve probably run into an issue stubbing or mocking the requests. In this post we’ll learn how to stub different AWS Services with Sinon.JS so that you can properly test your scripts.

UPDATE: AWS Lambda now supports Node v8.10, so we can use async/await instead of promises. The examples below still work with either v6.10 or v8.10, however, I recommend switching to async/await as they are more compact than promises. Read my post How To: Stub “.promise()” in AWS-SDK Node.js to learn how to deal with the .promise() method on aws-sdk services.

Let’s say you have a Lambda function that interacts with AWS’s SQS (Simple Queue Service). v6.10 of Node doesn’t support async/await, so you will most likely use promises if you don’t want to transpile your code or deal with callback hell. This means you need to Promisify an instance of the AWS SQS service. This is easy enough with:

Now you could stub SQS’s promisified methods with Sinon.js like this:

However, your code is most likely more complicated than that. If you have several reusable modules (maybe a message manager, for example) that can be used by multiple functions, then this becomes a problem. We can’t stub the AWS SDK until after it gets instantiated. If our test scripts try to stub SQS before it is instantiated by your message manager module (for instance), then it will throw an exception.

The solution is really simple

Since Node caches all of your module calls (any time you require something), all you need to do is wrap your SQS instantiation and promisification into a separate module like so:

Name it something like “sqs-service.js” and then simply require it in any module that needs it. Then when you require it in your test script, you can stub it like this:

Now this version of SQS gets cached because it was required before any other modules were called. All subsequent modules will use this stubbed version allowing you to create extremely complex tests that can return any response you’d like from the AWS service. Pretty sweet!

NOTE: The test above is using the serverless-mocha-plugin to wrap Lambda functions that use the Serverless framework. This allows you to simulate running your Lambda functions with environment variables and different events and then run assertions on the results.

Are you using MySQL, Redis, or other databases in your Lambda project? Learn how to Reuse Database Connections in AWS Lambda.

Tags: , , , , , , , ,

Did you like this post? 👍  Do you want more? 🙌  Follow me on Twitter or check out some of the projects I’m working on.

5 thoughts on “How To: Stub AWS Services in Lambda Functions using Serverless, Sinon.JS and Promises”

  1. Thanks for the article. I’ve used the similar approach to test lambda function with serverless-mocha-plugin and it is great to test in local. However, can you make the same test to work against live AWS service? If so, what is required to set to run it against AWS service? Attempting to do it, I get some error and would like to hear how you do it.

    1. Are you trying to stub AWS services in a serverless-mocha-plugin test running against a live Lambda function? That wouldn’t be possible since it invokes the remote Lambda function in the AWS environment. Locally you can inject dependencies and the Node environment will cache them. When invoking a live Lambda, it just sends the event and then you can test the results.

      When I write end-to-end tests for live functions, I typically test for basic connectivity to dependent services. My other local tests should cover all the more complex functionality. For example, if I have a function that uses an SQS queue and RDS, I’d send in a test event that required those services, then just test against a failure. If those services weren’t easily exposed, I might add a route or an event that would check connectivity to those services. I often do this with connections that you can freeze, like MySQL or Redis. That way I can use the same test to check how many times a function has been reused.

  2. Thanks for your reply. Since I saw the option of –stage and –region in serverless-mocha-plugin, I thought it might be possible. However, I just found out that it’s not possible and serverless-mocha-plugin also noted that live Lambda function is not supported yet.
    What you described are the approaches I was thinking but was not sure I was going to the right direction.
    I have functional tests which tests lots of possible inputs against Lambda functions locally. Now, I’ll create another tests simply invoking live lamda in AWS and check out the result as well as end to end test using HTTP Gateway.

  3. I’m trying this same exact code, promisfying my S3 service and stubbing listObjectsV2Async. But I keep getting this error – MissingRequiredParameter: Missing required key ‘Bucket’ in params.

    const AWS = require(‘aws-sdk’) // AWS SDK
    const Promise = require(‘bluebird’) // Promise library

    // Export promisified S3 library
    module.exports = Promise.promisifyAll(new AWS.S3())

    const stubbedList = sinon.stub(s3, ‘listObjectsV2Async’);

    stubbedList.withArgs({ Bucket: ‘my-test-bucket’, Prefix: ‘my/prefix’ }).resolves(myReturnObj);
    const result = await myAsyncFunc(); // The real call to listObjectsV2Async happens in here and keeps throwing that error

    Any suggestions?

    1. Hi Andrew,

      It’s hard to tell without seeing the full code, but the error you’re getting is coming from S3, not your stub. It looks like your call to listObjectsV2Async isn’t being stubbed correctly in your myAsyncFunc(). Try logging the “s3.listObjectsV2Async” function and make sure that it is actually getting stubbed.

      – Jeremy

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.