UNPKG

discord-bot-cdk-construct

Version:

A quick CDK Construct for creating a serverless Discord bot in AWS!

351 lines (249 loc) 11.6 kB
# aws-sdk-mock AWSome mocks for Javascript aws-sdk services. [![Build Status](https://img.shields.io/travis/dwyl/aws-sdk-mock/master.svg?style=flat-square)](https://travis-ci.org/dwyl/aws-sdk-mock) [![codecov.io](https://img.shields.io/codecov/c/github/dwyl/aws-sdk-mock/master.svg?style=flat-square)](http://codecov.io/github/dwyl/aws-sdk-mock?branch=master) [![Dependency Status](https://david-dm.org/dwyl/aws-sdk-mock.svg?style=flat-square)](https://david-dm.org/dwyl/aws-sdk-mock) [![devDependency Status](https://david-dm.org/dwyl/aws-sdk-mock/dev-status.svg?style=flat-square)](https://david-dm.org/dwyl/aws-sdk-mock#info=devDependencies) [![Known Vulnerabilities](https://snyk.io/test/github/dwyl/aws-sdk-mock/badge.svg?targetFile=package.json&style=flat-square)](https://snyk.io/test/github/dwyl/aws-sdk-mock?targetFile=package.json) <!-- broken see: https://github.com/dwyl/aws-sdk-mock/issues/161#issuecomment-444181270 [![NPM](https://nodei.co/npm-dl/aws-sdk-mock.png?months=3)](https://nodei.co/npm/aws-sdk-mock/) --> This module was created to help test AWS Lambda functions but can be used in any situation where the AWS SDK needs to be mocked. If you are *new* to Amazon WebServices Lambda (*or need a refresher*), please checkout our our ***Beginners Guide to AWS Lambda***: <https://github.com/dwyl/learn-aws-lambda> * [Why](#why) * [What](#what) * [Getting Started](#how) * [Documentation](#documentation) * [Background Reading](#background-reading) ## Why? Testing your code is *essential* everywhere you need *reliability*. Using stubs means you can prevent a specific method from being called directly. In our case we want to prevent the actual AWS services to be called while testing functions that use the AWS SDK. ## What? Uses [Sinon.js](https://sinonjs.org/) under the hood to mock the AWS SDK services and their associated methods. ## *How*? (*Usage*) ### *install* `aws-sdk-mock` from NPM ```sh npm install aws-sdk-mock --save-dev ``` ### Use in your Tests #### Using plain JavaScript ```js const AWS = require('aws-sdk-mock'); AWS.mock('DynamoDB', 'putItem', function (params, callback){ callback(null, 'successfully put item in database'); }); AWS.mock('SNS', 'publish', 'test-message'); // S3 getObject mock - return a Buffer object with file data AWS.mock('S3', 'getObject', Buffer.from(require('fs').readFileSync('testFile.csv'))); /** TESTS **/ AWS.restore('SNS', 'publish'); AWS.restore('DynamoDB'); AWS.restore('S3'); // or AWS.restore(); this will restore all the methods and services ``` #### Using TypeScript ```typescript import AWSMock from 'aws-sdk-mock'; import AWS from 'aws-sdk'; import { GetItemInput } from 'aws-sdk/clients/dynamodb'; beforeAll(async (done) => { //get requires env vars done(); }); describe('the module', () => { /** TESTS below here **/ it('should mock getItem from DynamoDB', async () => { // Overwriting DynamoDB.getItem() AWSMock.setSDKInstance(AWS); AWSMock.mock('DynamoDB', 'getItem', (params: GetItemInput, callback: Function) => { console.log('DynamoDB', 'getItem', 'mock called'); callback(null, {pk: 'foo', sk: 'bar'}); }) const input:GetItemInput = { TableName: '', Key: {} }; const dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'}); expect(await dynamodb.getItem(input).promise()).toStrictEqual({ pk: 'foo', sk: 'bar' }); AWSMock.restore('DynamoDB'); }); it('should mock reading from DocumentClient', async () => { // Overwriting DynamoDB.DocumentClient.get() AWSMock.setSDKInstance(AWS); AWSMock.mock('DynamoDB.DocumentClient', 'get', (params: GetItemInput, callback: Function) => { console.log('DynamoDB.DocumentClient', 'get', 'mock called'); callback(null, {pk: 'foo', sk: 'bar'}); }); const input:GetItemInput = { TableName: '', Key: {} }; const client = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'}); expect(await client.get(input).promise()).toStrictEqual({ pk: 'foo', sk: 'bar' }); AWSMock.restore('DynamoDB.DocumentClient'); }); }); ``` #### Sinon You can also pass Sinon spies to the mock: ```js const updateTableSpy = sinon.spy(); AWS.mock('DynamoDB', 'updateTable', updateTableSpy); // Object under test myDynamoManager.scaleDownTable(); // Assert on your Sinon spy as normal assert.isTrue(updateTableSpy.calledOnce, 'should update dynamo table via AWS SDK'); const expectedParams = { TableName: 'testTableName', ProvisionedThroughput: { ReadCapacityUnits: 1, WriteCapacityUnits: 1 } }; assert.isTrue(updateTableSpy.calledWith(expectedParams), 'should pass correct parameters'); ``` **NB: The AWS Service needs to be initialised inside the function being tested in order for the SDK method to be mocked** e.g for an AWS Lambda function example 1 will cause an error `region not defined in config` whereas in example 2 the sdk will be successfully mocked. Example 1: ```js const AWS = require('aws-sdk'); const sns = AWS.SNS(); const dynamoDb = AWS.DynamoDB(); exports.handler = function(event, context) { // do something with the services e.g. sns.publish } ``` Example 2: ```js const AWS = require('aws-sdk'); exports.handler = function(event, context) { const sns = AWS.SNS(); const dynamoDb = AWS.DynamoDB(); // do something with the services e.g. sns.publish } ``` Also note that if you initialise an AWS service inside a callback from an async function inside the handler function, that won't work either. Example 1 (won't work): ```js exports.handler = function(event, context) { someAsyncFunction(() => { const sns = AWS.SNS(); const dynamoDb = AWS.DynamoDB(); // do something with the services e.g. sns.publish }); } ``` Example 2 (will work): ```js exports.handler = function(event, context) { const sns = AWS.SNS(); const dynamoDb = AWS.DynamoDB(); someAsyncFunction(() => { // do something with the services e.g. sns.publish }); } ``` ### Nested services It is possible to mock nested services like `DynamoDB.DocumentClient`. Simply use this dot-notation name as the `service` parameter to the `mock()` and `restore()` methods: ```js AWS.mock('DynamoDB.DocumentClient', 'get', function(params, callback) { callback(null, {Item: {Key: 'Value'}}); }); ``` **NB: Use caution when mocking both a nested service and its parent service.** The nested service should be mocked before and restored after its parent: ```js // OK AWS.mock('DynamoDB.DocumentClient', 'get', 'message'); AWS.mock('DynamoDB', 'describeTable', 'message'); AWS.restore('DynamoDB'); AWS.restore('DynamoDB.DocumentClient'); // Not OK AWS.mock('DynamoDB', 'describeTable', 'message'); AWS.mock('DynamoDB.DocumentClient', 'get', 'message'); // Not OK AWS.restore('DynamoDB.DocumentClient'); AWS.restore('DynamoDB'); ``` ### Don't worry about the constructor configuration Some constructors of the aws-sdk will require you to pass through a configuration object. ```js const csd = new AWS.CloudSearchDomain({ endpoint: 'your.end.point', region: 'eu-west' }); ``` Most mocking solutions with throw an `InvalidEndpoint: AWS.CloudSearchDomain requires an explicit 'endpoint' configuration option` when you try to mock this. **aws-sdk-mock** will take care of this during mock creation so you **won't get any configuration errors**!<br> If configurations errors still occur it means you passed wrong configuration in your implementation. ### Setting the `aws-sdk` module explicitly Project structures that don't include the `aws-sdk` at the top level `node_modules` project folder will not be properly mocked. An example of this would be installing the `aws-sdk` in a nested project directory. You can get around this by explicitly setting the path to a nested `aws-sdk` module using `setSDK()`. Example: ```js const path = require('path'); const AWS = require('aws-sdk-mock'); AWS.setSDK(path.resolve('../../functions/foo/node_modules/aws-sdk')); /** TESTS **/ ``` ### Setting the `aws-sdk` object explicitly Due to transpiling, code written in TypeScript or ES6 may not correctly mock because the `aws-sdk` object created within `aws-sdk-mock` will not be equal to the object created within the code to test. In addition, it is sometimes convenient to have multiple SDK instances in a test. For either scenario, it is possible to pass in the SDK object directly using `setSDKInstance()`. Example: ```js // test code const AWSMock = require('aws-sdk-mock'); import AWS from 'aws-sdk'; AWSMock.setSDKInstance(AWS); AWSMock.mock('SQS', /* ... */); // implementation code const sqs = new AWS.SQS(); ``` ### Configuring promises If your environment lacks a global Promise constructor (e.g. nodejs 0.10), you can explicitly set the promises on `aws-sdk-mock`. Set the value of `AWS.Promise` to the constructor for your chosen promise library. Example (if Q is your promise library of choice): ```js const AWS = require('aws-sdk-mock'), Q = require('q'); AWS.Promise = Q.Promise; /** TESTS **/ ``` ## Documentation ### `AWS.mock(service, method, replace)` Replaces a method on an AWS service with a replacement function or string. | Param | Type | Optional/Required | Description | | :------------- | :------------- | :------------- | :------------- | | `service` | string | Required | AWS service to mock e.g. SNS, DynamoDB, S3 | | `method` | string | Required | method on AWS service to mock e.g. 'publish' (for SNS), 'putItem' for 'DynamoDB' | | `replace` | string or function | Required | A string or function to replace the method | ### `AWS.restore(service, method)` Removes the mock to restore the specified AWS service | Param | Type | Optional/Required | Description | | :------------- | :------------- | :------------- | :------------- | | `service` | string | Optional | AWS service to restore - If only the service is specified, all the methods are restored | | `method` | string | Optional | Method on AWS service to restore | If `AWS.restore` is called without arguments (`AWS.restore()`) then all the services and their associated methods are restored i.e. equivalent to a 'restore all' function. ### `AWS.remock(service, method, replace)` Updates the `replace` method on an existing mocked service. | Param | Type | Optional/Required | Description | | :------------- | :------------- | :------------- | :------------- | | `service` | string | Required | AWS service to mock e.g. SNS, DynamoDB, S3 | | `method` | string | Required | method on AWS service to mock e.g. 'publish' (for SNS), 'putItem' for 'DynamoDB' | | `replace` | string or function | Required | A string or function to replace the method | ### `AWS.setSDK(path)` Explicitly set the require path for the `aws-sdk` | Param | Type | Optional/Required | Description | | :------------- | :------------- | :------------- | :------------- | | `path` | string | Required | Path to a nested AWS SDK node module | ### `AWS.setSDKInstance(sdk)` Explicitly set the `aws-sdk` instance to use | Param | Type | Optional/Required | Description | | :------------- | :------------- | :------------- | :------------- | | `sdk` | object | Required | The AWS SDK object | ## Background Reading * [Mocking using Sinon.js](http://sinonjs.org/docs/) * [AWS Lambda](https://github.com/dwyl/learn-aws-lambda) **Contributions welcome! Please submit issues or PRs if you think of anything that needs updating/improving**