or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

AWS SDK Mock

AWS SDK Mock provides comprehensive mocking functionality for the AWS SDK for JavaScript (v2) using Sinon.js under the hood. It enables developers to mock AWS services and their methods for testing purposes, allowing simulation of AWS service responses without making actual API calls.

Package Information

  • Package Name: aws-sdk-mock
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install aws-sdk-mock --save-dev

Core Imports

import AWS from 'aws-sdk-mock';
// or
import * as AWS from 'aws-sdk-mock';

For CommonJS:

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

Basic Usage

import AWSMock from 'aws-sdk-mock';
import AWS from 'aws-sdk';

// Set SDK instance for TypeScript environments
AWSMock.setSDKInstance(AWS);

// Mock a service method with a string response
AWSMock.mock('SNS', 'publish', 'message sent successfully');

// Mock with a function for custom behavior
AWSMock.mock('DynamoDB', 'putItem', function(params, callback) {
  callback(null, { Item: { id: '123', name: 'test' } });
});

// Mock nested services (e.g., DynamoDB.DocumentClient)
AWSMock.mock('DynamoDB.DocumentClient', 'get', function(params, callback) {
  callback(null, { Item: { pk: 'foo', sk: 'bar' } });
});

// Use mocked services
const sns = new AWS.SNS();
const result = await sns.publish({ Message: 'test' }).promise();

// Restore mocks
AWSMock.restore('SNS', 'publish');  // Restore specific method
AWSMock.restore('DynamoDB');        // Restore all methods on service
AWSMock.restore();                  // Restore all mocks

Architecture

AWS SDK Mock is built around several key components:

  • Service Mocking: Intercepts AWS service constructor calls using Sinon stubs
  • Method Replacement: Replaces individual service methods with custom implementations
  • Lazy Evaluation: Services are only mocked when first instantiated
  • Nested Service Support: Handles dot-notation services like DynamoDB.DocumentClient
  • Multiple Replacement Types: Supports strings, functions, objects, and Sinon spies/stubs
  • Promise Integration: Full support for both callback and promise-based AWS SDK patterns

Capabilities

Service Mocking

Core functionality for mocking AWS services and their methods with flexible replacement options.

/**
 * Stubs the service and registers the method that needs to be mocked
 * @param service - AWS service to mock (e.g. 'DynamoDB', 'SNS', 'DynamoDB.DocumentClient')
 * @param method - Method on AWS service to mock (e.g. 'putItem', 'publish')
 * @param replace - Replacement for the method (string, function, or object)
 * @returns Mock replacement object with stub information
 */
function mock<C extends ClientName>(
  service: NestedClientName,
  method: NestedMethodName,
  replace: ReplaceFn<C, MethodName<ClientName>>
): Replace<ClientName, MethodName<ClientName>>;

function mock<C extends ClientName, M extends MethodName<C> & string>(
  service: C,
  method: M,
  replace: ReplaceFn<C, MethodName<ClientName>>
): Replace<ClientName, MethodName<ClientName>>;

/**
 * Updates the replace method on an existing mocked service
 * @param service - AWS service to remock
 * @param method - Method on AWS service to remock
 * @param replace - New replacement for the method
 */
function remock<C extends ClientName>(
  service: NestedClientName,
  method: NestedMethodName,
  replace: ReplaceFn<C, MethodName<ClientName>>
): void;

function remock<C extends ClientName, M extends MethodName<C> & string>(
  service: C,
  method: M,
  replace: ReplaceFn<ClientName, MethodName<ClientName>>
): void;

/**
 * Restores the mocks for just one method on a service, the entire service, or all mocks
 * @param service - Optional service to restore (if not provided, restores all)
 * @param method - Optional method to restore (if not provided, restores all methods on service)
 */
function restore<C extends ClientName>(service?: NestedClientName, method?: NestedMethodName): void;
function restore<C extends ClientName>(service?: C, method?: MethodName<C>): void;

/**
 * Explicitly sets the aws-sdk to be mocked by path
 * @param path - Path to the aws-sdk module
 * @returns Promise that resolves when SDK is loaded
 */
function setSDK(path: string): Promise<void>;

/**
 * Explicitly sets the aws-sdk instance to be used
 * @param sdk - The aws-sdk instance to use for mocking
 */
function setSDKInstance(sdk: any): void;

Usage Examples:

import AWSMock from 'aws-sdk-mock';
import AWS from 'aws-sdk';
import sinon from 'sinon';

// Set SDK instance
AWSMock.setSDKInstance(AWS);

// String replacement - returns the string as response
AWSMock.mock('SNS', 'publish', 'success message');

// Function replacement - full control over response
AWSMock.mock('DynamoDB', 'getItem', function(params, callback) {
  if (params.Key.id.S === 'test') {
    callback(null, { Item: { id: { S: 'test' }, name: { S: 'Test Item' } } });
  } else {
    callback(new Error('Item not found'));
  }
});

// Object replacement - returns the object
AWSMock.mock('S3', 'getObject', { Body: Buffer.from('file content') });

// Sinon spy for behavior verification
const spy = sinon.spy();
AWSMock.mock('Lambda', 'invoke', spy);
// Later verify: expect(spy.calledOnce).toBe(true);

// Nested service mocking
AWSMock.mock('DynamoDB.DocumentClient', 'scan', { Items: [{ id: 1 }] });

// Promise-based usage works automatically
const dynamodb = new AWS.DynamoDB();
const result = await dynamodb.getItem(params).promise();

Promise Configuration

The Promise constructor used by mocked services can be customized:

/**
 * Promise constructor used by mocked services
 * Can be set to custom Promise implementation
 */
let Promise: PromiseConstructor;

Usage Example:

import AWSMock from 'aws-sdk-mock';
import Q from 'q';

// Configure custom Promise library (e.g., Q, Bluebird)
AWSMock.Promise = Q.Promise;

Types

import { type Request, type AWSError } from 'aws-sdk/lib/core.js';
import AWS from 'aws-sdk/clients/all';
import { type SinonStub } from 'sinon';

/**
 * Available AWS service names (from aws-sdk package)
 */
type ClientName = keyof typeof AWS;

/**
 * AWS client instance type
 */
type Client<C extends ClientName> = InstanceType<(typeof AWS)[C]>;

/**
 * Utility type to extract method names from client
 */
type ExtractMethod<T extends {}> = { 
  [K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never 
};

/**
 * Available method names for a specific AWS client
 */
type MethodName<C extends ClientName> = keyof ExtractMethod<Client<C>>;

/**
 * AWS method type
 */
type Method<C extends ClientName, M extends MethodName<C>> = ExtractMethod<Client<C>>[M];

/**
 * AWS request parameters type for a specific service method
 */
type AWSRequest<C extends ClientName, M extends MethodName<C>> = 
  Method<C, M> extends AWSMethod<infer P, any> ? P : never;

/**
 * AWS callback type for a specific service method
 */
type AWSCallback<C extends ClientName, M extends MethodName<C>> = 
  Method<C, M> extends AWSMethod<any, infer D>
    ? {
        (err: undefined, data: D): void;
        (err: AWSError, data?: undefined): void;
      }
    : any;

/**
 * Replacement function type for mock/remock methods
 * Can be a function, string, or object
 */
type ReplaceFn<C extends ClientName, M extends MethodName<C>> =
  | ((params: AWSRequest<C, M>, options: any, callback: AWSCallback<C, M>) => any)
  | string
  | object;

/**
 * AWS method interface
 */
interface AWSMethod<P, D> {
  (params: P, callback?: Callback<D>): Request<D, AWSError>;
  (callback?: Callback<D>): Request<D, AWSError>;
}

/**
 * AWS callback interface
 */
type Callback<D> = (err: AWSError | undefined, data: D) => void;

/**
 * Mock replacement object returned by mock() function
 */
type Replace<C extends ClientName, M extends MethodName<C>> = {
  replace: ReplaceFn<C, M>;
  stub?: SinonStub<any, any> & Partial<MaybeSoninProxy>;
};

/**
 * Sinon proxy detection interface
 */
type MaybeSoninProxy = {
  _isMockFunction: boolean;
  isSinonProxy: boolean;
};

/**
 * Service object type
 */
interface Service {
  Constructor: new (...args: any[]) => any;
  methodMocks: MethodMock;
  invoked: boolean;
  clients?: Client<ClientName>[];
  stub?: SinonStub<any, any>;
}

/**
 * Method mock mapping
 */
type MethodMock = {
  [key: string]: Replace<ClientName, MethodName<ClientName>>;
};

/**
 * Nested service names (supports dot notation like 'DynamoDB.DocumentClient')
 */
type NestedClientName = ClientName | (string & {});
type NestedMethodName = string & {};

Advanced Features

Parameter Validation

AWS SDK Mock respects the AWS SDK's parameter validation settings:

import AWS from 'aws-sdk';
import AWSMock from 'aws-sdk-mock';

// Set SDK instance
AWSMock.setSDKInstance(AWS);

// Enable parameter validation (validates params before calling mock)
AWS.config.paramValidation = true;

AWSMock.mock('DynamoDB', 'putItem', function(params, callback) {
  // This will only be called if params pass AWS SDK validation
  callback(null, { success: true });
});

Stream Support

For services that return streams (like S3), you can mock with Readable streams:

import { Readable } from 'stream';
import AWSMock from 'aws-sdk-mock';

// Mock S3 getObject with a stream
const stream = new Readable();
stream.push('file content');
stream.push(null);

AWSMock.mock('S3', 'getObject', stream);

// Or return a stream from a function
AWSMock.mock('S3', 'getObject', function(params, callback) {
  const stream = new Readable();
  stream._read = function() {
    this.push('dynamic content');
    this.push(null);
  };
  callback(null, { Body: stream });
});

Sinon Integration

Full integration with Sinon.js for advanced testing patterns:

import sinon from 'sinon';
import AWSMock from 'aws-sdk-mock';
import AWS from 'aws-sdk';

// Set SDK instance
AWSMock.setSDKInstance(AWS);

// Use Sinon spies for call verification
const publishSpy = sinon.spy();
AWSMock.mock('SNS', 'publish', publishSpy);

// Make calls...
const sns = new AWS.SNS();
await sns.publish({ Message: 'test' }).promise();

// Verify behavior
expect(publishSpy.calledOnce).toBe(true);
expect(publishSpy.calledWith({ Message: 'test' })).toBe(true);

// Use Sinon stubs for complex behavior
const dynamoStub = sinon.stub();
dynamoStub.withArgs(sinon.match({ TableName: 'users' }))
  .returns({ Items: [{ id: 1, name: 'Alice' }] });
dynamoStub.withArgs(sinon.match({ TableName: 'products' }))
  .returns({ Items: [{ id: 1, name: 'Laptop' }] });

AWSMock.mock('DynamoDB.DocumentClient', 'scan', dynamoStub);

Error Handling

Mock error responses for testing error handling paths:

import AWSMock from 'aws-sdk-mock';
import AWS from 'aws-sdk';

// Set SDK instance
AWSMock.setSDKInstance(AWS);

// Mock with error callback
AWSMock.mock('DynamoDB', 'putItem', function(params, callback) {
  const error = new Error('ConditionalCheckFailedException');
  error.code = 'ConditionalCheckFailedException';
  callback(error);
});

// Mock with promise rejection
AWSMock.mock('S3', 'getObject', function(params, callback) {
  callback(new Error('NoSuchKey'));
});

// Test error handling
const s3 = new AWS.S3();
try {
  await s3.getObject({ Bucket: 'test', Key: 'missing' }).promise();
} catch (error) {
  expect(error.message).toBe('NoSuchKey');
}

Testing Best Practices

Essential patterns for effective testing with AWS SDK Mock:

import AWSMock from 'aws-sdk-mock';
import AWS from 'aws-sdk';

describe('AWS Service Tests', () => {
  beforeEach(() => {
    // Set SDK instance for each test
    AWSMock.setSDKInstance(AWS);
  });

  afterEach(() => {
    // Always restore after each test
    AWSMock.restore();
  });

  it('should handle DynamoDB operations', async () => {
    // Mock the service method
    AWSMock.mock('DynamoDB.DocumentClient', 'put', function(params, callback) {
      expect(params.TableName).toBe('users');
      callback(null, { success: true });
    });

    // Test your code that uses AWS services
    const result = await userService.createUser({ name: 'Alice' });
    expect(result.success).toBe(true);
  });

  it('should handle nested service restoration order', () => {
    // Mock nested service first
    AWSMock.mock('DynamoDB.DocumentClient', 'get', 'document-result');
    AWSMock.mock('DynamoDB', 'describeTable', 'table-result');
    
    // Restore in reverse order
    AWSMock.restore('DynamoDB');
    AWSMock.restore('DynamoDB.DocumentClient');
  });
});