mrkeyoor.com_
Sat 08 Aug 22:53 UTC
npmTestingupdated 08 Aug 2026

aws-sdk-client-mock

aws-sdk-client-mock is a unit-testing helper for the modular AWS SDK for JavaScript v3. It replaces an AWS client's send method with a Sinon stub, then lets a test return or reject typed responses for particular Command classes and partial inputs. It can mock every instance of a client class or one client object, record sent commands, and provide sequential or computed responses. It never contacts AWS and does not simulate a service, credentials, request signing, retries, middleware, or network behavior.

Verdict

A focused and readable unit-test tool for ordinary AWS SDK v3 Command flows. Keep a smaller set of emulator or real-service tests for everything the send stub deliberately skips, and watch compatibility before upgrading AWS SDK or your test runner.

API stability4/5The fluent core has stayed recognizable across releases: mockClient returns a stub with on, onAnyCommand, resolves, rejects, callsFake, inspection, reset, and restore methods. Version 4.1 added commandCall without disturbing those fundamentals, and the README publishes a clear compatibility boundary for AWS SDK releases before and after 3.363.0. The weaker point is structural typing against fast-moving Smithy packages, which can break compilation without any change to this library's own API.
Docs5/5The README covers class and instance mocks, partial and strict matching, sequential results, errors, fakes, inspection, reset versus restore, Jest and Vitest matchers, DynamoDB DocumentClient, S3 streams, multipart uploads, paginators, Lambda, and Mocha. Its caveats section documents three non-obvious ordering and TypeScript traps with fixes. A generated API reference is live as well. Few testing helpers explain their failure modes this directly.
Maintenance2/5The repository is not archived and its pushed-at timestamp is January 2026, with newer pull requests still arriving. However, 4.1.0 and the latest main-branch runtime commit date to October 2024. Open issues report trouble with newer AWS SDK versions, Jest 30.3, Vitest matchers, ESM, and Sinon dependency placement, while update pull requests remain open. That is enough activity to avoid calling it dead, but not enough to assume prompt compatibility fixes.
Ecosystem4/5The package recorded 3,145,218 downloads in the measured npm week, has 910 GitHub stars, and is recommended in an AWS Developer Tools Blog post linked from the README. It works independently of a test runner and has a companion aws-sdk-client-mock-jest package with Jest and Vitest entries. The main constraint is scope: extensions and integrations center on AWS SDK v3 send calls, not service emulation, signing, waiters, or presigned URLs.

Use it if

  • Your Node.js or TypeScript unit tests call AWS SDK v3 clients through client.send(new Command(input)) and should run without credentials or network access
  • You want command-aware response types and partial input matching instead of hand-stubbing send in every test
  • You need to inspect which AWS Commands were sent, their inputs, and their order while keeping the production client API intact
  • You use Jest, Vitest, Mocha, or another test runner and are comfortable managing Sinon-backed mock lifecycle explicitly
Skip it if

Setup reality

Install aws-sdk-client-mock as a development dependency beside the AWS SDK v3 client packages your code already uses. It includes Sinon, @types/sinon, and tslib as runtime dependencies; you do not need to install Sinon unless you want to share a custom sandbox. No AWS account, region, credentials, emulator, or config file is needed because the helper replaces Client#send. That convenience creates the first surprise: an unconfigured mock resolves to undefined, so a forgotten behavior may fail later in application code rather than at the send call. Reset each mock before or after every test or behavior and call history will leak between cases. restore is different from reset: restore puts the real send method back, which can accidentally allow network-bound code to run. TypeScript failures often come from duplicated @smithy/types versions across AWS packages; the documented remedies are aligning every @aws-sdk package, reinstalling, or forcing one Smithy type version. Explicit AwsClientStub annotations also require strictFunctionTypes or strict mode. Matching is partial by default, with strict matching enabled by the third argument. Declaration order matters because broader matchers registered later can eclipse earlier specific ones. Class and instance mocks have a separate ordering trap: create instance mocks first and the class mock last. Jest and Vitest matchers live in the separate aws-sdk-client-mock-jest package, and Vitest must import its /vitest entry. Under Mocha, create the mock inside beforeEach rather than global scope to avoid test files replacing one another's stubs. S3 GetObject bodies require @smithy/util-stream and sdkStreamMixin, while @aws-sdk/lib-storage Upload tests must mock each command that Upload sends internally. These are unit stubs, not a substitute for a small integration-test layer against AWS or LocalStack.

Patterns

Return a typed response for one Commandmock-command-response

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

const snsMock = mockClient(SNSClient);
snsMock.on(PublishCommand).resolves({ MessageId: 'msg-123' });

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

Without a matching behavior, the mocked send method resolves to undefined. Configure every path your code reads from.

Match partial or exact Command inputmatch-command-input

snsMock
  .on(PublishCommand, { Message: 'hello' })
  .resolves({ MessageId: 'partial' });

snsMock
  .on(PublishCommand, {
    TopicArn: 'arn:aws:sns:us-east-1:111111111111:events',
    Message: 'strict',
  }, true)
  .resolves({ MessageId: 'exact' });

Input matching is partial by default. Pass true as the third argument only when extra input fields should make the matcher fail.

Declare broad behavior before specific behaviororder-mock-behaviors

snsMock
  .resolves({ MessageId: 'any-command' })
  .on(PublishCommand)
  .resolves({ MessageId: 'any-publish' })
  .on(PublishCommand, { Message: 'priority' })
  .resolves({ MessageId: 'priority-publish' });

Register wider matchers first. A broad matcher declared later takes precedence and can make an earlier specific response unreachable.

Model consecutive responsesreturn-sequential-results

snsMock
  .on(PublishCommand)
  .resolvesOnce({ MessageId: 'first' })
  .resolvesOnce({ MessageId: 'second' })
  .resolves({ MessageId: 'later' });

After the once-only responses are consumed, the final resolves value becomes the fallback for subsequent matching calls.

Simulate an AWS-style failurereject-with-aws-error

const error = new Error('rate exceeded');
error.name = 'ThrottlingException';
Object.assign(error, { $metadata: { httpStatusCode: 429 } });

snsMock
  .on(PublishCommand)
  .rejects(error);

rejects accepts a string, Error, or error-like object and normalizes it to Error. Set the name and metadata your application actually branches on.

Build a response from input and client configurationcompute-response-from-client

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

getClient returns the client associated with the current call. The fake does not run AWS middleware or validate the input.

Mock only one client instancemock-one-client-instance

const primary = new SNSClient({ region: 'us-east-1' });
const secondary = new SNSClient({ region: 'eu-west-1' });

const primaryMock = mockClient(primary);
primaryMock.resolves({ MessageId: 'primary-only' });

await primary.send(new PublishCommand({ Message: 'test' }));
// secondary.send remains real unless a class mock also exists

If mixing instance and class mocks, create instance mocks first and declare the class mock last. The reverse order leaves surprising instances unmocked.

Keep tests isolated and restore real behaviorreset-and-restore

const snsMock = mockClient(SNSClient);

beforeEach(() => {
  snsMock.reset();
});

afterAll(() => {
  snsMock.restore();
});

reset clears behavior and history but keeps send mocked. resetHistory preserves behavior. restore removes the stub and puts the original send method back.

Inspect matching Command callsinspect-command-calls

const publishes = snsMock.commandCalls(
  PublishCommand,
  { Message: 'hello' }
);

expect(publishes).toHaveLength(2);
expect(publishes[0].args[0].input.TopicArn).toContain(':events');

const secondPublish = snsMock.commandCall(1, PublishCommand);

commandCall uses a zero-based index among calls of that Command, while call uses a zero-based index among every recorded send call.

Add Jest or Vitest Command matchersuse-test-runner-matchers

// Jest: import 'aws-sdk-client-mock-jest';
// Vitest uses the entry below:
import 'aws-sdk-client-mock-jest/vitest';

expect(snsMock).toHaveReceivedCommandWith(
  PublishCommand,
  { Message: expect.stringContaining('hello') }
);

Install aws-sdk-client-mock-jest separately. Import its /vitest entry under Vitest; the default entry targets Jest.

Mock DynamoDBDocumentClient commandsmock-dynamodb-document-client

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

const ddbMock = mockClient(DynamoDBDocumentClient);
ddbMock.on(QueryCommand).resolves({
  Items: [{ pk: 'USER#1', sk: 'PROFILE' }],
});

const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
await ddb.send(new QueryCommand({ TableName: 'app' }));

Mock DynamoDBDocumentClient, not the lower-level DynamoDBClient, when production code sends lib-dynamodb Command classes.

Return an SDK-compatible S3 streammock-s3-stream-body

import { Readable } from 'node:stream';
import { sdkStreamMixin } from '@smithy/util-stream';
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';

const s3Mock = mockClient(S3Client);
const body = sdkStreamMixin(Readable.from(['hello']));

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

const s3 = new S3Client({});
const result = await s3.send(new GetObjectCommand({ Bucket: 'b', Key: 'k' }));
expect(await result.Body?.transformToString()).toBe('hello');

Install @smithy/util-stream. A plain Node stream lacks the transformToString and related mixin methods attached by the real SDK.

Alternatives

PackageRegistryPick it when
sinonnpmStub a known client instance's send method directly when command matching and AWS-specific types add more machinery than value
aws-sdk-mocknpmMock service methods in a codebase that still uses the monolithic AWS SDK for JavaScript v2
testcontainersnpmRun a LocalStack container when tests must exercise SDK serialization, endpoints, retries, and service-like behavior