Creating Mocks for an AWS Service

Mock functions for any AWS service to be used in unit tests

Published: 19 July 2020

AWS mocks unit testing jest 

Secrets Manager


Secrets Manager is an AWS service which allows us to store and retrieve secrets in AWS. For the purposes of this post, lets assume we are writing some unit tests and need to stub out the actual API calls to this service.

How does Secret Manager code works


Let's say we want to get the value of a secret whose value we have already stored in AWS, we can do it like so:

//sample.js
const aws = require("aws-sdk")

const secretsManager = new aws.SecretsManager()

async function getMySecret(secretName) {
  try {
    const secret = await secretsManager
      .getSecretValue({ SecretId: secretName })
      .promise()
    return JSON.parse(secret)
  } catch (err) {
    return err
  }
}

module.exports = { getMySecret }

To create a mock of the above function so no API call is actually made to secrets manager, we need to stub it out like so:

(1) Create an actual mock for aws-sdk and put it in __mocks__/aws-sdk.js file at the root of our project. This will allow jest to pick it up and use it instead of the actual aws-sdk.

// __mocks__/aws-sdk.js

class AWS {
  static SecretsManager = class {
    getSecretValue = jest.fn(secretName =>
      Promise.resolve(JSON.stringify({ ARN: "custom-arn2", Name: secretName }))
    )
  }
}

module.exports = AWS

Couple of things to note here:

  • static keyword is used before the SecretsManager class to signify that it can be instantiated without needing to instantiante our main AWS class. This is done to mimick actual AWS class structure.
  • we have replaced the actual function with jest.fn which is a fake function and which returns a promise with our fake payload. This will help us make assertions.
  • Its returning a JSON stringified object which is what the actual secrets manager call returns

(2) Now, in our test file we can then tell jest to use our mock:

//sample.test.js

const aws = require("aws-sdk") // we still need to require this
const { getMySecret } = require("./sample.js")
jest.mock("aws-sdk") // jest will automatically find the mock

it("gets the correct secret value and converts it to javascript object", async () => {
  const result = await getMySecret("joking-secret")
  expect(result).toEqual({ ARN: "custom-arn2", Name: "joking-secret" })
})

Downsides of creating mocks for whole module

(1) Its hard to mimick any other states or values that the API can return - like error values or empty response. This makes difficult to test how our function will handle such scenarios.

(2) What if we only want to create fake function for just a single test and for the rest of the tests we DO actually want to make the API call (this is a very probable scenario in API testing).

For these situations, we need to create a one time fake function.

//sample.test.js

const aws = require("aws-sdk") // we still need to require this
const { getMySecret } = require("./sample.js")

const secretsManager = new aws.SecretsManager()

it("gets the correct secret value and converts it to javascript object", async () => {
  //create a one time fake for success
  secretsManager.getSecretValue = jest.fn().mockImplementationOnce((secretName)=>{
    return Promise.resolve(JSON.stringify({ ARN: "custom-arn2", Name: secretName });
  });
  const result = await getMySecret("joking-secret")
  expect(result).toEqual({ ARN: "custom-arn2", Name: "joking-secret" })
})


it("fails to get secret value and returns error", async () => {
  //create one time fake for error
  secretsManager.getSecretValue = jest.fn().mockImplementationOnce((secretName)=>{
    return Promise.reject(new Error('I am an error'));
  });
  const result = await getMySecret("joking-secret")
  expect(result).toBe({message:'I am an error'})
})

This solves our problem since we are creating single instances of fake function with jest.fn().mockImplementationOnce.

There is nothing stopping us from also creating the __mocks__ folder and only import it in relevant test files while in other places we can use single time fake functions or let the API calls run as original functions.


AWS Secrets Manager Reference

Jest Manual Mocks for Modules

Jest Mock Implementation Once Reference