Anytime you build Node applications it is highly suggested that your cover your code with tests. When your code interacts with 3rd party API’s such as AWS you will most certainly want to mock/stub your calls in order to prevent external calls (if you actually want to do external calls, these are called integration tests not unit tests.
If you are using Jest, one solution is utilize the built in support for manual mocks. I have found the usage of manual mocks invaluable while testing 3rd party API’s such as the AWS. Keep in mind just because I am using manual mocks this will remove the need for using libraries like SinonJs (a JavaScript framework for creating stubs/mocks/spies).
The way that manual mocks work in Jest is as follows (from the Jest website’s documentation).
Manual mocks are defined by writing a module in a
__mocks__/
subdirectory immediately adjacent to the module. For example, to mock a module calleduser
in themodels
directory, create a file calleduser.js
and put it in themodels/__mocks__
directory. Note that the__mocks__
folder is case-sensitive, so naming the directory__MOCKS__
will break on some systems. If the module you are mocking is a node module (eg:fs
), the mock should be placed in the__mocks__
directory adjacent tonode_modules
(unless you configuredroots
to point to a folder other than the project root).
In my case I want to mock out the usage of the AWS-SDK for Node.
To do this I created a __mocks__ folder at the root of my solution. I then created a aws-sdk.js file inside this folder.
Now that I have my mocks folder created with a aws-sdk.js file I am able to consume my manual mock in my jest test by simply referencing the aws-sdk via a require('aws-sdk')
command.
const AWS = require('./aws-sdk');
With declaration of AWS above my code is able to a use the NPM package during normal usage, or my aws-sdk.js mock when running under the Jest context.
Below is a small sample of the code I have inside my aws-sdk.js file for my manual mock.
const stubs = require('./aws-stubs'); const AWS = {}; // This here is to allow/prevent runtime errors if you are using // AWS.config to do some runtime configuration of the library. // If you do not need any runtime configuration you can omit this. AWS.config = { setPromisesDependency: (arg) => {} }; AWS.S3 = function() { } // Because I care about using the S3 service's which are part of the SDK // I need to setup the correct identifier. // AWS.S3.prototype = { ...AWS.S3.prototype, // Stub for the listObjectsV2 method in the sdk listObjectsV2(params){ const stubPromise = new Promise((resolve, reject) => { // pulling in stub data from an external file to remove the noise // from this file. See the top line for how to pull this in resolve(stubs.listObjects); }); return { promise: () => { return stubPromise; } } } }; // Export my AWS function so it can be referenced via requires module.exports = AWS;
A few things to point out in the code above.
- I chose to use the promises implementation of the listObjectsV2. Because of this I need to return a promise method as my result on my listObjectsV2 function. I am sure there are other ways to accomplish this, but this worked and is pretty easy.
- My function is returning stub data, but this data is described in a separate file called aws-stubs.js which sites along side of my aws-sdk.js file. I went this route to remove the noise of having the stub data inside my aws-adk file. You can see a full example of this here.
Now that I have everything setup my tests will no longer attempt to hit the actually aws-sdk, but when running in non-test mode they will.
Till next time,