Gathering detailed insights and metrics for aws-sdk-client-mock-jest
Gathering detailed insights and metrics for aws-sdk-client-mock-jest
Gathering detailed insights and metrics for aws-sdk-client-mock-jest
Gathering detailed insights and metrics for aws-sdk-client-mock-jest
aws-sdk-client-mock
Easy and powerful mocking of AWS SDK v3 Clients
aws-sdk-client-mock-vitest
Custom matchers for AWS SDK Client mock to be used in vitest
@aws-sdk/client-sso
AWS SDK for JavaScript Sso Client for Node.js, Browser and React Native
aws-sdk-mock
Functions to mock the JavaScript aws-sdk
AWS JavaScript SDK v3 mocks for easy unit testing. 🖋️ Typed 🔬 Tested 📄 Documented 🛠️ Maintained
npm install aws-sdk-client-mock-jest
55.4
Supply Chain
57.9
Quality
85.4
Maintenance
100
Vulnerability
97.3
License
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
811 Stars
231 Commits
40 Forks
6 Watching
2 Branches
17 Contributors
Updated on 20 Nov 2024
Minified
Minified + Gzipped
TypeScript (99.83%)
Shell (0.09%)
JavaScript (0.07%)
Cumulative downloads
Total Downloads
Last day
-4.3%
76,627
Compared to previous day
Last week
13%
409,876
Compared to previous week
Last month
4.9%
1,558,212
Compared to previous month
Last year
114.8%
15,493,005
Compared to previous year
Library recommended by the AWS SDK for JavaScript team - see the introductory post on the AWS blog.
Features:
In action:
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:
1import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; 2 3const sns = new SNSClient({}); 4const result = await sns.send(new PublishCommand({ 5 TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic', 6 Message: 'My message', 7})); 8 9console.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.
1npm install -D aws-sdk-client-mock
Warning
If you are getting type errorsArgument of type 'typeof SomeClient' is not assignable to parameter of type...
see instructions here.
@aws-sdk/* | aws-sdk-client-mock |
---|---|
≥ 3.363.0 | ≥ 3.x |
< 3.363.0 | 2.x |
CommonJS:
1const {mockClient} = require('aws-sdk-client-mock');
TypeScript / ES6:
1import {mockClient} from 'aws-sdk-client-mock';
Create mock for all instances or for given instance of the AWS SDK Client:
1const snsMock = mockClient(SNSClient); 2 3const dynamoDB = new DynamoDBClient({}); 4const 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:
1snsMock.onAnyCommand().resolves({}); 2 3// same as: 4 5snsMock.resolves({});
Specify mock behavior on receiving given command only:
1snsMock 2 .on(PublishCommand) 3 .resolves({ 4 MessageId: '12345678-1111-2222-3333-111122223333', 5 });
Specify mock behavior on receiving given command with given payload only:
1snsMock 2 .on(PublishCommand, { 3 TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic', 4 Message: 'My message', 5 }) 6 .resolves({ 7 MessageId: '12345678-4444-5555-6666-111122223333', 8 });
Not all payload parameters must be defined to match
(you can force strict matching by passing third param strict: true
):
1snsMock 2 .on(PublishCommand, { 3 Message: 'My message', 4 }) 5 .resolves({ 6 MessageId: '12345678-4444-5555-6666-111122223333', 7 });
Specify mock behavior on receiving given payload only:
1snsMock 2 .onAnyCommand({ 3 TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic', 4 Message: 'My message', 5 }) 6 .resolves({ 7 MessageId: '12345678-4444-5555-6666-111122223333', 8 });
Multiple behaviors (for different commands and payloads) may be specified for a single mock:
1snsMock 2 .resolves({ // default for any command 3 MessageId: '12345678-1111-2222-3333-111122223333' 4 }) 5 .on(PublishCommand) 6 .resolves({ // default for PublishCommand 7 MessageId: '12345678-4444-5555-6666-111122223333' 8 }) 9 .on(PublishCommand, { 10 TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic', 11 Message: 'My message', 12 }) 13 .resolves({ // for PublishCommand with given input 14 MessageId: '12345678-7777-8888-9999-111122223333', 15 });
Specify chained behaviors - next behaviors for consecutive calls:
1snsMock 2 .on(PublishCommand) 3 .resolvesOnce({ // for the first command call 4 MessageId: '12345678-1111-1111-1111-111122223333' 5 }) 6 .resolvesOnce({ // for the second command call 7 MessageId: '12345678-2222-2222-2222-111122223333' 8 }) 9 .resolves({ // for further calls 10 MessageId: '12345678-3333-3333-3333-111122223333' 11 });
Specify mock throwing an error:
1snsMock 2 .rejects('mocked rejection');
1const throttlingError = new Error('mocked rejection'); 2throttlingError.name = 'ThrottlingException'; 3 4snsMock 5 .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:
1snsMock 2 .callsFake(input => { 3 if (input.Message === 'My message') { 4 return {MessageId: '12345678-1111-2222-3333-111122223333'}; 5 } else { 6 throw new Error('mocked rejection'); 7 } 8 });
Specify custom mock function for a specific command (chained behavior):
1snsMock 2 .on(PublishCommand) 3 .callsFake(input => { 4 if (input.Message === 'My message') { 5 return {MessageId: '12345678-1111-2222-3333-111122223333'}; 6 } else { 7 throw new Error('mocked rejection'); 8 } 9 });
Specify result based on Client configuration, i.e. region:
1snsMock 2 .on(PublishCommand) 3 .callsFake(async (input, getClient) => { 4 const client = getClient(); 5 const region = await client.config.region(); 6 return {MessageId: region.substring(0, 2)}; 7 });
Together with resolvesOnce()
, you can also use rejectsOnce()
and callsFakeOnce()
to specify consecutive behaviors.
You can mock the DynamoDBDocumentClient
just like any other Client:
1import {DynamoDBClient} from '@aws-sdk/client-dynamodb'; 2import {DynamoDBDocumentClient, QueryCommand} from '@aws-sdk/lib-dynamodb'; 3 4const ddbMock = mockClient(DynamoDBDocumentClient); 5ddbMock.on(QueryCommand).resolves({ 6 Items: [{pk: 'a', sk: 'b'}], 7}); 8 9const dynamodb = new DynamoDBClient({}); 10const ddb = DynamoDBDocumentClient.from(dynamodb); 11 12const query = await ddb.send(new QueryCommand({ 13 TableName: 'mock', 14}));
To mock @aws-sdk/lib-storage
Upload
you need to mock all commands
used under the hood:
1import {S3Client, CreateMultipartUploadCommand, UploadPartCommand} from '@aws-sdk/client-s3'; 2import {Upload} from "@aws-sdk/lib-storage"; 3 4const s3Mock = mockClient(S3Client); 5 6// for big files upload: 7s3Mock.on(CreateMultipartUploadCommand).resolves({UploadId: '1'}); 8s3Mock.on(UploadPartCommand).resolves({ETag: '1'}); 9 10// for small files upload: 11s3ClientMock.on(PutObjectCommand).callsFake(async (input, getClient) => { 12 getClient().config.endpoint = () => ({hostname: ""}) as any; 13 return {}; 14}); 15 16const s3Upload = new Upload({ 17 client: new S3Client({}), 18 params: { 19 Bucket: 'mock', 20 Key: 'test', 21 Body: 'x'.repeat(6 * 1024 * 1024), // 6 MB 22 }, 23}); 24 25s3Upload.on('httpUploadProgress', (progress) => { 26 console.log(progress); 27}); 28 29await 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:
1s3Mock.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:
1s3Mock.on(UploadPartCommand).rejects();
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:
1import {GetObjectCommand, S3Client} from '@aws-sdk/client-s3'; 2import {sdkStreamMixin} from '@smithy/util-stream'; 3import {mockClient} from 'aws-sdk-client-mock'; 4import {Readable} from 'stream'; 5import {createReadStream} from 'fs'; 6 7const s3Mock = mockClient(S3Client); 8 9it('mocks get object', async () => { 10 // create Stream from string 11 const stream = new Readable(); 12 stream.push('hello world'); 13 stream.push(null); // end of stream 14 15 // alternatively: create Stream from file 16 // const stream = createReadStream('./test/data.txt'); 17 18 // wrap the Stream with SDK mixin 19 const sdkStream = sdkStreamMixin(stream); 20 21 s3Mock.on(GetObjectCommand).resolves({Body: sdkStream}); 22 23 const s3 = new S3Client({}); 24 25 const getObjectResult = await s3.send(new GetObjectCommand({Bucket: '', Key: ''})); 26 27 const str = await getObjectResult.Body?.transformToString(); 28 29 expect(str).toBe('hello world'); 30});
To mock a paginated operation results, simply mock the corresponding Command:
1import {DynamoDBClient, paginateQuery, QueryCommand} from '@aws-sdk/client-dynamodb'; 2import {marshall} from '@aws-sdk/util-dynamodb'; 3 4const dynamodbMock = mockClient(DynamoDBClient); 5dynamodbMock.on(QueryCommand).resolves({ 6 Items: [ 7 marshall({pk: 'a', sk: 'b'}), 8 marshall({pk: 'c', sk: 'd'}), 9 ], 10}); 11 12const dynamodb = new DynamoDBClient({}); 13const paginator = paginateQuery({client: dynamodb}, {TableName: 'mock'}); 14 15const items = []; 16for await (const page of paginator) { 17 items.push(...page.Items || []); 18}
The AWS SDK v3 gives an option to use it similarly to v2 SDK,
with command method call instead of send()
:
1import {SNS} from '@aws-sdk/client-sns'; 2 3const sns = new SNS({}); 4const result = await sns.publish({ 5 TopicArn: 'arn:aws:sns:us-east-1:111111111111:MyTopic', 6 Message: 'My message', 7});
Although this approach is not recommended by AWS, those calls can be mocked in the standard way:
1import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; 2 3const snsMock = mockClient(SNSClient); 4snsMock 5 .on(PublishCommand) 6 .resolves({ 7 MessageId: '12345678-1111-2222-3333-111122223333', 8 });
Notice that in mocks you still need to use SNSClient
, not SNS
,
as well as Command
classes.
Inspect received calls:
1snsMock.calls(); // all received calls 2snsMock.call(0); // first received call
Get calls of a specified command:
1snsMock.commandCalls(PublishCommand)
Get calls of a specified command with given payload
(you can force strict matching by passing third param strict: true
):
1snsMock.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:
1const snsSendStub = snsMock.send;
The Client mock exposes three Sinon.js stub
methods:
reset()
, resetHistory()
, and restore()
.
The reset()
method resets the mock state and behavior.
The Client will continue to be mocked, only now with a clean mock instance,
without any behavior (set with methods like on(...).resolves(...)
) and calls history.
You should call clientMock.reset()
before or after every test
(using beforeEach()
/ beforeAll()
from your test framework)
to keep tests independent from each other.
The resetHistory()
only clear mocked client calls history
that you access with mockedClient.call(...)
and mockedClient.calls()
.
The behavior is preserved.
The restore()
removes the mock altogether,
restoring the normal behavior of client.send()
.
You can also pass custom Sinon Sandbox
with mockClient(client, { sandbox: mySandbox })
to manage all mocks lifecycle at once.
Custom Jest matchers simplify verification that the mocked Client was called with given Commands.
Matchers are published as a separate package. Install it:
1npm install -D aws-sdk-client-mock-jest
Usage (notice the import
):
1import 'aws-sdk-client-mock-jest'; 2 3// a PublishCommand was sent to SNS 4expect(snsMock).toHaveReceivedCommand(PublishCommand); 5 6// at least one command was sent to SNS 7expect(snsMock).toHaveReceivedAnyCommand(); 8 9// two PublishCommands were sent to SNS 10expect(snsMock).toHaveReceivedCommandTimes(PublishCommand, 2); 11 12// a PublishCommand with Message "hello world" was sent to SNS 13expect(snsMock).toHaveReceivedCommandWith( 14 PublishCommand, {Message: 'hello world'} 15); 16 17// a PublishCommand with Message containing "hello" was sent to SNS 18expect(snsMock).toHaveReceivedCommandWith( 19 PublishCommand, {Message: expect.stringContaining('hello')} 20); 21 22// the second command sent to SNS was a PublishCommand with Message "hello world" 23expect(snsMock).toHaveReceivedNthCommandWith( 24 2, PublishCommand, {Message: 'hello world'} 25); 26 27// the second PublishCommand sent to SNS had Message "hello world" 28expect(snsMock).toHaveReceivedNthSpecificCommandWith( 29 2, PublishCommand, {Message: 'hello world'} 30);
Shorter aliases exist, like toReceiveCommandTimes()
.
Use those matchers with Vitest:
1import 'aws-sdk-client-mock-jest/vitest'; 2import { expect } from 'vitest'; 3 4// a PublishCommand was sent to SNS 5expect(snsMock).toHaveReceivedCommand(PublishCommand);
To use the matchers outside of Jest, you can pull in the expect library separately and add it to the global scope directly, e.g.:
1const {expect} = require("expect"); 2(globalThis as any).expect = expect; 3require("aws-sdk-client-mock-jest");
See the full API Reference.
Example below uses Jest as a test framework, but mocks will work with any testing library.
Let's take a simple Lambda function that takes a list of messages, sends them to SNS topic and returns message IDs:
1import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; 2 3const snsTopicArn = process.env.SNS_TOPIC_ARN || ''; 4 5const sns = new SNSClient({}); 6 7export const handler = async (event: Event): Promise<string[]> => { 8 const promises = event.messages.map(async (msg, idx) => { 9 const publish = await sns.send(new PublishCommand({ 10 TopicArn: snsTopicArn, 11 Message: msg, 12 })); 13 return publish.MessageId!; 14 }); 15 16 return await Promise.all(promises); 17}; 18 19interface Event { 20 messages: string[]; 21}
Then the tests could look like this:
1import {mockClient} from 'aws-sdk-client-mock'; 2import {PublishCommand, SNSClient} from '@aws-sdk/client-sns'; 3import {handler} from '../src'; 4 5const snsMock = mockClient(SNSClient); 6 7/** 8 * To be sure that unit tests are independent from each other, 9 * reset mock behavior between the tests. 10 */ 11beforeEach(() => { 12 snsMock.reset(); 13}); 14 15it('message IDs are returned', async () => { 16 snsMock.on(PublishCommand).resolves({ 17 MessageId: '12345678-1111-2222-3333-111122223333', 18 }); 19 20 const result = await handler({ 21 messages: ['one', 'two', 'three'] 22 }); 23 24 expect(result).toHaveLength(3); 25 expect(result[0]).toBe('12345678-1111-2222-3333-111122223333'); 26}); 27 28it('SNS Client is called with PublishCommand', async () => { 29 snsMock.on(PublishCommand).resolves({ 30 MessageId: '111-222-333', 31 }); 32 33 await handler({ 34 messages: ['qq', 'xx'] 35 }); 36 37 expect(snsMock).toHaveReceivedCommandTimes(PublishCommand, 2); 38});
For more examples, see the unit tests.
Note
Those instructions refer to@smithy/types
used by AWS SDK v3.363.0 and above. For version below 3.363.0, perform the same steps for the@aws-sdk/types
package.
If you have multiple versions of @smithy/types
installed in your project,
you can get type errors similar to this:
TS2345: Argument of type 'typeof DynamoDBDocumentClient' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
Type 'typeof DynamoDBDocumentClient' is not assignable to type 'ClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
The types of 'prototype.middlewareStack.concat' are incompatible between these types.
Type '<InputType extends ServiceInputTypes, OutputType extends ServiceOutputTypes>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<...>' is not assignable to type '<InputType extends ServiceInputTypes, OutputType extends MetadataBearer>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>'.
Types of parameters 'from' and 'from' are incompatible.
Property 'identify' is missing in type 'MiddlewareStack<InputType, OutputType>' but required in type 'MiddlewareStack<InputType, ServiceOutputTypes>'.
Run npm ls @smithy/types
/ pnpm why @smithy/types
/ yarn why @smithy/types
and check if you have more than one version of the package installed.
To solve this, go through the steps until one works:
@aws-sdk/*
packages point to the same version,@aws-sdk/*
packages from package.json
, run npm install
/ pnpm install
/ yarn install
,
restore @aws-sdk/*
packages in package.json
, and run install again,@smithy/types
to your dev dependencies in the latest version,@smithy/types
version with npm overrides,
pnpm overrides, or yarn resolutions,npm ls @smithy/types
/ pnpm why @smithy/types
/ yarn why @smithy/types
.If you need to explicitly type the mock variable,
you can use AwsClientStub
type:
1import {AwsClientStub, mockClient} from 'aws-sdk-client-mock' 2import {S3Client} from "@aws-sdk/client-s3"; 3 4const mock: AwsClientStub<S3Client> = mockClient(S3Client);
The AwsClientStub
type works only with tsconfig
option
strictFunctionTypes=true
or (strict=true
) in tsconfig.json
file.
See details in #167.
Wider Command matchers must be declared first, otherwise, they will take precedence over previous ones.
In this case, all PublishCommand
sends will return message ID 222
:
1snsMock 2 .on(PublishCommand, myInput).resolves({MessageId: '111'}) 3 .on(PublishCommand).resolves({MessageId: '222'});
If the order of the declarations is switched, sends with input matching myInput
will return ID 111
and all others 222
.
It works similarly with onAnyCommand()
.
When you create both a Client type mock and a specific Client instance mock(s), you need to declare type mock last. Otherwise, the other instances will not be mocked.
Right now if you create a mock for the Client type, and then mock a specific instance of this Client, with the order of mocking as here:
1const sns1 = new SNSClient({}); // not mocked 2 3mockClient(SNSClient).resolves({MessageId: '123'}); 4 5const sns2 = new SNSClient({}); // mocked 6mockClient(sns2).resolves({MessageId: '456'}); 7 8const sns3 = new SNSClient({}); // not mocked
Declaring mocks in this order will fix it:
1const sns1 = new SNSClient({}); // mocked - default 2 3const sns2 = new SNSClient({}); // mocked 4mockClient(sns2).resolves({MessageId: '456'}); 5 6mockClient(SNSClient).resolves({MessageId: '123'}); 7 8const sns3 = new SNSClient({}); // mocked - default
PRs to fix this are welcome.
When testing with Mocha, call mockClient()
in the beforeEach()
method, not in the global scope,
to prevent overriding the mock between test files.
See this
for more details.
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
12 commit(s) and 15 issue activity found in the last 90 days -- score normalized to 10
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 3/29 approved changesets -- score normalized to 1
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
12 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More