@mapbox/aws-sdk-jest
DefinitelyTyped icon, indicating that this package has TypeScript declarations provided by the separate @types/mapbox__aws-sdk-jest package

0.0.4 • Public • Published

@mapbox/aws-sdk-jest

A generic Jest mock for the aws-sdk module.

How to set it up

  1. Install this module as a devDependency npm install -D @mapbox/aws-sdk-jest.
  2. Add a file in your repository at test/__mocks__/aws-sdk.js
  3. The file should look like this:
'use strict';

module.exports = require('@mapbox/aws-sdk-jest');

The mocking methods it provides

AWS.spyOn(service, method)

Returns a Jest mock function for an AWS SDK method call like s3.getObject(). You provide your own .mockImplementation() or .mockReturnValue() by setting it on the mock function in your test.

AWS.spyOnPromise(service, method, response)

Again, returns a Jest mock function for an AWS SDK method call like s3.getObject(). However, it anticipates that your code under test will use the .promise() method. The response argument is optional.

  • If you do not provide a response, the .promise() will resolve with an empty object.
  • If you provide an Error object as response, the .promise() will reject with the error.
  • If you provide any other thing as response, the .promise() will resolve with that thing.

AWS.spyOnEachPage(service, method, pages)

Also returns a Jest mock function for an AWS SDK method call that supports pagination, like s3.listObjectsV2(). This time, it anticipates that your code under test will use the .eachPage() method.

The pages argument is required, and must be an array representing the pages that the code will observe during the test. If any of the pages are an Error object, then that error will be returned to the .eachPage() caller after sending non-error pages.

AWS.clearAllMocks()

This clears all your mocks and should be used in afterEach() functions.

Examples

Here is an example function that maybe you would like to test.

const getThing = async () => {
  const s3 = new AWS.S3({ region: 'us-east-1' });
  return await s3.getObject({ Bucket: 'my', Key: 'thing' }).promise();
};

Here is a set of tests for it.

'use strict';

const AWS = require('aws-sdk');

describe('getting things', () => {
  afterEach(() => AWS.clearAllMocks());

  it('returns the thing', async () => {
    const get = AWS.spyOn('S3', 'getObject').mockReturnValue({
      promise: () => Promise.resolve('foo')
    });

    const thing = await getThing();
    expect(thing).toEqual('foo');
  });

  it('sets up an s3 client in the right region', async () => {
    AWS.spyOn('S3', 'getObject').mockReturnValue({
      promise: () => Promise.resolve('foo')
    });

    await getThing();
    expect(AWS.S3).toHaveBeenCalledWith({ region: 'us-east-1' });
  });

  it('asks for the right thing', async () => {
    const get = AWS.spyOn('S3', 'getObject').mockReturnValue({
      promise: () => Promise.resolve('foo')
    });

    await getThing();
    expect(get).toHaveBeenCalledWith({ Bucket: 'my', Key: 'thing' });
  });

  it('can mock .promise() directly', async () => {
    const get = AWS.spyOnPromise('S3', 'getObject', { Body: 'foo' });
    const result = await getThing();
    expect(result).toStrictEqual({ Body: 'foo' });
    expect(get).toHaveBeenCalledWith({ Bucket: 'my', Key: 'thing' });
  });

  it('can handle mocked .promise() errors', async () => {
    const get = AWS.spyOnPromise('S3', 'getObject', new Error('foo'));
    await expect(() => getThing()).rejects.toThrow('foo');
  });
});

If your code uses .eachPage(), there's a way to mock that, too. Say you're testing this function:

const listThings = () =>
  new Promise((resolve, reject) => {
    const s3 = new AWS.S3({ region: 'us-east-1' });
    let things = [];
    s3.listObjectsV2({ Bucket: 'myBucket' }).eachPage((err, data, done) => {
      if (err) return reject(err);
      if (!data) return resolve(things);
      things = things.concat(data.Contents);
      done();
    });
  });

You can mock the method call by providing a list of pages that should be returned.

'use strict';

const AWS = require('aws-sdk');

describe('listing things', () => {
  afterEach(() => AWS.clearAllMocks());

  it('can mock .eachPage directly', async () => {
    const list = AWS.spyOnEachPage('S3', 'listObjectsV2', [
      { Contents: [1, 2, 3] },
      { Contents: [4, 5, 6] }
    ]);

    const result = await listThings();
    expect(result).toStrictEqual([1, 2, 3, 4, 5, 6]);
    expect(AWS.S3).toHaveBeenCalledWith({ region: 'us-east-1' });
    expect(list).toHaveBeenCalledWith({ Bucket: 'myBucket' });
  });

  it('can mock .eachPage errors on any page', async () => {
    AWS.spyOnEachPage('S3', 'listObjectsV2', [
      { Contents: [1, 2, 3] },
      new Error('foo')
    ]);

    await expect(() => listThings()).rejects.toThrow('foo');
  });
});

Some notes

  • If you try to mock a method twice, you will get an error.
  • For nested clients like AWS.DynamoDB.DocumentClient, you can mock methods like this: AWS.spyOn('DynamoDB.DocumentClient', 'get').
  • You should be familiar with the AWS.Request object, because if your code needs to set special expectations for .promise(), .eachPage(), or .on(), then you're going to have to use AWS.spyOn() and provide your own implementations for those Request methods.
  • If your application is using dyno ... don't! Use AWS.DynamoDB.DocumentClient, instead! But if you must, you'll need to mock the nested paths of the aws-sdk that dyno uses:
    jest.mock('aws-sdk/lib/dynamodb/set', () => jest.fn());
    jest.mock('aws-sdk/lib/dynamodb/converter', () => jest.fn());

Readme

Keywords

none

Package Sidebar

Install

npm i @mapbox/aws-sdk-jest

Weekly Downloads

231

Version

0.0.4

License

BSD-2-Clause

Unpacked Size

19.1 kB

Total Files

8

Last publish

Collaborators

  • mbx-npm-ci-production
  • mbx-npm-ci-staging
  • mbx-npm-advanced-actions-production
  • mbx-npm-advanced-actions-staging
  • mbx-npm-09-production
  • mbx-npm-08-production
  • mbx-npm-07-production
  • mbx-npm-06-production
  • mbx-npm-05-production
  • mbx-npm-04-production
  • mbx-npm-03-production
  • mbx-npm-02-production
  • mbx-npm-01-production
  • mbx-npm-02-staging
  • mapbox-npm-01
  • mapbox-npm-02
  • mapbox-npm-07
  • mapbox-npm-03
  • mapbox-npm-04
  • mapbox-npm-09
  • mapbox-npm-05
  • mapbox-npm-06
  • mapbox-npm-08
  • mapbox-npm-advanced-actions
  • mapbox-npm-ci
  • mapbox-npm
  • mapbox-admin
  • mapbox-machine-user