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: 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 join my mailing list too. I’ll email you when I post more stuff like this! 📪

Sign Up!

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

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.