How To: Stub “.promise()” in AWS-SDK Node.js

Since AWS released support for Node v8.10 in Lambda, I was able to refactor Lambda API to use async/await instead of Bluebird promises. The code is not only much cleaner now, but I was able to remove a lot of unnecessary overhead as well. As part of the refactoring, I decided to use AWS-SDK’s native promise implementation by appending .promise() to the end of an S3 getObject call. This works perfectly in production and the code is super compact and simple:

The issue came with stubbing the call using Sinon.js. With the old promise method, I was using promisifyAll() to wrap new AWS.S3() and then stubbing the getObjectAsync method. If you’re not familiar with stubbing AWS services, read my post: How To: Stub AWS Services in Lambda Functions using Serverless, Sinon.JS and Promises.

The old way looked like this (condensed for readability):

Any test calls to S3.getObjectAsync that use the specified arguments resolves a promise and returns the data. Getting rid of promises seemed straightforward:

But then I obviously got the error: S3.getObject(…).promise is not a function.”

As I always do when I encounter something like this, I Googled it. I found a GitHub issue in the aws-sdk-js repo that sort of gave a solution: https://github.com/aws/aws-sdk-js/issues/1973. The problem is that stubbing (or mocking) services should be entirely contained within your tests. You should avoid adding anything to your production code, which this solution would have required.

The Answer

After a bit of experimentation I finally realized that Sinon.js completely rewires stubbed functions, essentially eliminating any underlying prototype methods. In this case, the .promise() method no longer existed. Lucky for us, we can return anything we want from Sinon.js stubs, including (🥁 drumroll please) functions!

If we examine how our code is calling the getObject method, we see that .promise() is chained to it. This means that getObject has to return a promise method in order for the code to execute.

We can return an object from our stub (rather than resolve it) using the .returns() method provided with stubs:

Now getObject returns a promise() method and the code works. The only problem is that our stub isn’t returning any data. This is easily fixed by returning the data from inside the promise() method:

Voilà! Now your stub works correctly and we didn’t have to alter any of our production code to make it work.

You might have noticed that this “promise” doesn’t actually return a promise. For our implementation with await it doesn’t matter, because it will assume a value as a resolved promise. However, if you needed it to return a promise (like if you were still using promise chains), you could simply wrap your data in a Promise.resolve() and that should work.

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. You can sign up for my WEEKLY newsletter too. You'll get links to my new posts (like this one), industry happenings, project updates and much more! 📪

Sign Up for my WEEKLY email newsletter


I respect your privacy and I will NEVER sell, rent or share your email address.

5 thoughts on “How To: Stub “.promise()” in AWS-SDK Node.js”

  1. Hi Jeremy,

    Thanks for the great blog series. Learning a lot from reading it. Had a question for you. Why use bluebird when we have native support for promises from node 6 ?

    1. Hi Gaurav,

      Great question. I typically don’t use Bluebird anymore since Lambda now supports Node 8.10 and we can use async/await instead. However, in the examples above, I used Bluebird because of its promisifyAll() function. This will take a library and (assuming it follows the standard (error, response) callback style) create duplicate methods that return promises instead of callbacks. In the case above where we did const S3 = Promise.promisifyAll(new AWS.S3()), we now get a method called getObjectAsync() (in addition to the standard getObject method) that will return a promise rather than using a standard callback.

      Otherwise we’d have to write our own functions that wrapped the asynchronous calls in a promise. Bluebird does that automatically for us. However, if you were just using promises and didn’t need to promisify another library, native promises would be just fine. No need for the extra dependency.

      Hope that helps,
      Jeremy

  2. I think you can just use resolves instead of returns


    stub.withArgs({Bucket: 'my-test-bucket', Key: 'test/test.txt'}).resolves({
    AcceptRanges: 'bytes',
    LastModified: new Date('2018-04-25T13:32:58.000Z'),
    ContentLength: 23,
    ETag: '"ae771fbbba6a74eeeb77754355831713"',
    ContentType: 'text/plain',
    Metadata: {},
    Body: Buffer.from('Test file\n')
    });

    1. Hi Andrew,

      Not if you are using the .promise() method in your calls. The stub won’t resolve that, which is why you need to return an object with a promise method.

      – 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.