MCPcopy Create free account
hub / github.com/m-radzikowski/aws-sdk-client-mock

github.com/m-radzikowski/aws-sdk-client-mock @v4.1.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v4.1.0 ↗ · + Follow
132 symbols 352 edges 27 files 37 documented · 28% 21 cross-repo links updated 5mo agov4.1.0 · 2024-10-15★ 90526 open issues

Browse by type

Functions 109 Types & classes 23
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

AWS SDK v3 Client mock

Easy and powerful mocking of AWS SDK v3 Clients.

npm aws-sdk-client-mock npm aws-sdk-client-mock-jest

Library recommended by the AWS SDK for JavaScript team - see the introductory post on the AWS blog.

Features:

  • 🌊  fluent interface - declaring behavior is short and readable
  • 🔍  matching options - defining mock behavior by Command type and/or its input payload
  • 🕵️  spying - checking if Commands were actually sent
  • 🃏  Jest matchers - easily verifying sent Commands
  • 🖋️  fully typed - same type control for declaring mock's behavior as when writing regular code
  • ✅  fully tested - reliable mocks help instead of impeding

In action:

aws-client-mock-example

Table of Contents

About AWS SDK v3

The AWS SDK for JavaScript version 3, is the new version of SDK to use in Node.js and browser. It comes with modular architecture and improved typing, thanks to being written in TypeScript.

The recommended way of using it is to create a Client and use it to send Commands.

For example, using SNS Client to publish a message to a topic looks like that:

import {PublishCommand, SNSClient} from '@aws-sdk/client-sns';

const sns = new SNSClient({});
const result = await sns.send(new PublishCommand({
  TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic',
  Message: 'My message',
}));

console.log(`Message published, id: ${result.MessageId}`);

This library provides an easy way to mock sending Commands and define returned results depending on the Command type and payload.

Usage

Install

npm install -D aws-sdk-client-mock

Warning
If you are getting type errors Argument of type 'typeof SomeClient' is not assignable to parameter of type... see instructions here.

Versions compatibility

@aws-sdk/* aws-sdk-client-mock
≥ 3.363.0 ≥ 3.x
< 3.363.0 2.x

Import

CommonJS:

const {mockClient} = require('aws-sdk-client-mock');

TypeScript / ES6:

import {mockClient} from 'aws-sdk-client-mock';

Mock

Create mock for all instances or for given instance of the AWS SDK Client:

const snsMock = mockClient(SNSClient);

const dynamoDB = new DynamoDBClient({});
const dynamoDBMock = mockClient(dynamoDB);

By default, mocked Client#send() method returns undefined.

Using the obtained mock instance, you can specify the mock behavior on receiving various commands to send.

See the AwsStub API Reference for all available methods or check out the examples below.

Specify default mock behavior:

snsMock.onAnyCommand().resolves({});

// same as:

snsMock.resolves({});

Specify mock behavior on receiving given command only:

snsMock
    .on(PublishCommand)
    .resolves({
        MessageId: '12345678-1111-2222-3333-111122223333',
    });

Specify mock behavior on receiving given command with given payload only:

snsMock
    .on(PublishCommand, {
        TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic',
        Message: 'My message',
    })
    .resolves({
        MessageId: '12345678-4444-5555-6666-111122223333',
    });

Not all payload parameters must be defined to match (you can force strict matching by passing third param strict: true):

snsMock
    .on(PublishCommand, {
        Message: 'My message',
    })
    .resolves({
        MessageId: '12345678-4444-5555-6666-111122223333',
    });

Specify mock behavior on receiving given payload only:

snsMock
    .onAnyCommand({
        TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic',
        Message: 'My message',
    })
    .resolves({
        MessageId: '12345678-4444-5555-6666-111122223333',
    });

Multiple behaviors (for different commands and payloads) may be specified for a single mock:

snsMock
    .resolves({ // default for any command
        MessageId: '12345678-1111-2222-3333-111122223333'
    })
    .on(PublishCommand)
    .resolves({ // default for PublishCommand
        MessageId: '12345678-4444-5555-6666-111122223333'
    })
    .on(PublishCommand, {
        TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic',
        Message: 'My message',
    })
    .resolves({ // for PublishCommand with given input
        MessageId: '12345678-7777-8888-9999-111122223333',
    });

Specify chained behaviors - next behaviors for consecutive calls:

snsMock
    .on(PublishCommand)
    .resolvesOnce({ // for the first command call
        MessageId: '12345678-1111-1111-1111-111122223333'
    })
    .resolvesOnce({ // for the second command call
        MessageId: '12345678-2222-2222-2222-111122223333'
    })
    .resolves({ // for further calls
        MessageId: '12345678-3333-3333-3333-111122223333'
    });

Specify mock throwing an error:

snsMock
    .rejects('mocked rejection');
const throttlingError = new Error('mocked rejection');
throttlingError.name = 'ThrottlingException';

snsMock
    .rejects(throttlingError);

In rejects(), you can pass a string, an Error instance, or an object with properties. In each case, it will be converted to an Error instance.

Specify custom mock function:

snsMock
    .callsFake(input => {
        if (input.Message === 'My message') {
            return {MessageId: '12345678-1111-2222-3333-111122223333'};
        } else {
            throw new Error('mocked rejection');
        }
    });

Specify custom mock function for a specific command (chained behavior):

snsMock
    .on(PublishCommand)
    .callsFake(input => {
        if (input.Message === 'My message') {
            return {MessageId: '12345678-1111-2222-3333-111122223333'};
        } else {
            throw new Error('mocked rejection');
        }
    });

Specify result based on Client configuration, i.e. region:

snsMock
    .on(PublishCommand)
    .callsFake(async (input, getClient) => {
        const client = getClient();
        const region = await client.config.region();
        return {MessageId: region.substring(0, 2)};
    });

Together with resolvesOnce(), you can also use rejectsOnce() and callsFakeOnce() to specify consecutive behaviors.

DynamoDB DocumentClient

You can mock the DynamoDBDocumentClient just like any other Client:

import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocumentClient, QueryCommand} from '@aws-sdk/lib-dynamodb';

const ddbMock = mockClient(DynamoDBDocumentClient);
ddbMock.on(QueryCommand).resolves({
    Items: [{pk: 'a', sk: 'b'}],
});

const dynamodb = new DynamoDBClient({});
const ddb = DynamoDBDocumentClient.from(dynamodb);

const query = await ddb.send(new QueryCommand({
  TableName: 'mock',
}));

Lib Storage Upload

To mock @aws-sdk/lib-storage Upload you need to mock all commands used under the hood:

import {S3Client, CreateMultipartUploadCommand, UploadPartCommand} from '@aws-sdk/client-s3';
import {Upload} from "@aws-sdk/lib-storage";

const s3Mock = mockClient(S3Client);

// for big files upload:
s3Mock.on(CreateMultipartUploadCommand).resolves({UploadId: '1'});
s3Mock.on(UploadPartCommand).resolves({ETag: '1'});

// for small files upload:
s3ClientMock.on(PutObjectCommand).callsFake(async (input, getClient) => {
  getClient().config.endpoint = () => ({hostname: ""}) as any;
  return {};
});

const s3Upload = new Upload({
    client: new S3Client({}),
    params: {
        Bucket: 'mock',
        Key: 'test',
        Body: 'x'.repeat(6 * 1024 * 1024), // 6 MB
    },
});

s3Upload.on('httpUploadProgress', (progress) => {
    console.log(progress);
});

await s3Upload.done();

This way, the Upload#done() will complete successfuly.

To cause a failure, you need to specify the rejects() behavior for one of the AWS SDK Commands used by the @aws-sdk/lib-storage.

For uploading a small file (under the defined multipart upload single part size), lib-storage sends a PutObjectCommand. To make it fail:

s3Mock.on(PutObjectCommand).rejects();

For bigger files, it makes a series of calls including CreateMultipartUploadCommand, UploadPartCommand, and CompleteMultipartUploadCommand. Making any of them fail will fail the upload:

s3Mock.on(UploadPartCommand).rejects();

S3 GetObjectCommand

AWS SDK wraps the stream in the S3 GetObjectCommand result to provide utility methods to parse it. To mock it, you need to install the @smithy/util-stream package and call the wrapping function sdkStreamMixin() on the stream you provide as the command output:

import {GetObjectCommand, S3Client} from '@aws-sdk/client-s3';
import {sdkStreamMixin} from '@smithy/util-stream';
import {mockClient} from 'aws-sdk-client-mock';
import {Readable} from 'stream';
import {createReadStream} from 'fs';

const s3Mock = mockClient(S3Client);

it('mocks get object', async () => {
    // create Stream from string
    const stream = new Readable();
    stream.push('hello world');
    stream.push(null); // end of stream

    // alternatively: create Stream from file
    // const stream = createReadStream('./test/data.txt');

    // wrap the Stream with SDK mixin
    const sdkStream = sdkStreamMixin(stream);

    s3Mock.on(GetObjectCommand).resolves({Body: sdkStream});

    const s3 = new S3Client({});

    const getObjectResult = await s3.send(new GetObjectCommand({Bucket: '', Key: ''}));

    const str = await getObjectResult.Body?.transformToString();

    expect(str).toBe('hello world');
});

Paginated operations

To mock a paginated operation results, simply mock the corresponding Command:

import {DynamoDBClient, paginateQuery, QueryCommand} from '@aws-sdk/client-dynamodb';
import {marshall} from '@aws-sdk/util-dynamodb';

const dynamodbMock = mockClient(DynamoDBClient);
dynamodbMock.on(QueryCommand).resolves({
    Items: [
        marshall({pk: 'a', sk: 'b'}),
        marshall({pk: 'c', sk: 'd'}),
    ],
});

const dynamodb = new DynamoDBClient({});
const paginator = paginateQuery({client: dynamodb}, {TableName: 'mock'});

const items = [];
for await (const page of paginator) {
    items.push(...page.Items || []);
}

SDK v2-style mocks

The AWS SDK v3 gives an option to use it similarly to v2 SDK, with command method call instead of send():

import {SNS} from '@aws-sdk/client-sns';

const sns = new SNS({});
const result = await sns.publish({
    TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic',
    Message: 'My message',
});

Although this approach is not recommended by AWS, those calls can be mocked in the standard way:

import {PublishCommand, SNSClient} from '@aws-sdk/client-sns';

const snsMock = mockClient(SNSClient);
snsMock
    .on(PublishCommand)
    .resolves({
        MessageId: '12345678-1111-2222-3333-111122223333',
    });

Notice that in mocks you still need to use SNSClient, not SNS, as well as Command classes.

Inspect

Inspect received calls:

snsMock.calls(); // all received calls
snsMock.call(0); // first received call

Get calls of a specified command:

snsMock.commandCalls(PublishCommand)

Get calls of a specified command with given payload (you can force strict matching by passing third param strict: true):

snsMock.commandCalls(PublishCommand, {Message: 'My message'})

Under the hood, the library uses Sinon.js stub. You can get the stub instance to configure and use it directly:

const snsSendStub = snsMock.send;

Reset and restore

The Client mock exposes three Sinon.js stub methods: reset(), resetHistory(), and restore().

The reset() method resets the mock

Extension points exported contracts — how you extend this code

Core symbols most depended-on inside this repo

Shape

Method 66
Function 43
Interface 17
Class 6

Languages

TypeScript100%

Modules by API surface

packages/aws-sdk-client-mock/src/awsClientStub.ts43 symbols
docs/assets/main.js42 symbols
packages/aws-sdk-client-mock-jest/src/types.ts19 symbols
packages/aws-sdk-client-mock-jest/src/jestMatchers.ts14 symbols
packages/aws-sdk-client-mock-jest/src/vitest.ts3 symbols
packages/aws-sdk-client-mock/src/sinon.ts2 symbols
packages/aws-sdk-client-mock/src/mockClient.ts2 symbols
packages/aws-sdk-client-mock-jest/vitest.serializer.ts2 symbols
packages/aws-sdk-client-mock-jest/src/jest.ts2 symbols
test-e2e/simple/run.ts1 symbols
packages/aws-sdk-client-mock/test/mockClient.test.ts1 symbols
docs/assets/icons.js1 symbols

For agents

$ claude mcp add aws-sdk-client-mock \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page