├── .eslintignore ├── .gitignore ├── .prettierignore ├── aws-sdk.js ├── license.md ├── package-lock.json ├── package.json ├── readme.md └── test ├── __mocks__ └── aws-sdk.js └── mock.test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .eslintignore -------------------------------------------------------------------------------- /aws-sdk.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AWS = jest.genMockFromModule('aws-sdk'); 4 | const _AWS = jest.requireActual('aws-sdk'); 5 | const traverse = require('traverse'); 6 | 7 | // Copy properties of the real `_AWS` object onto the mock `AWS` object. Service 8 | // classes are cloned as a new function with the real service as its prototype. 9 | // Everything else is copied with `=` (by value for primitives, by reference for 10 | // objects). Also, properties of service classes are copied with `=`, to ensure 11 | // we get nested clients like `DynamoDB.DocumentClient`. 12 | for (const key of Object.keys(_AWS)) { 13 | const realValue = _AWS[key]; 14 | if ( 15 | typeof realValue === 'function' && 16 | realValue.prototype instanceof _AWS.Service 17 | ) { 18 | AWS[key] = function () { 19 | return realValue.apply(this, arguments); 20 | }; 21 | AWS[key].prototype = realValue.prototype; 22 | Object.assign(AWS[key], realValue); 23 | } else { 24 | AWS[key] = realValue; 25 | } 26 | } 27 | 28 | const clients = {}; 29 | 30 | clients.get = (service) => { 31 | const split = service.split('.'); 32 | const real = traverse(_AWS).get(split); 33 | const mocked = traverse(AWS).get(split); 34 | 35 | if (!clients[service]) clients[service] = new real({}); 36 | const client = clients[service]; 37 | 38 | if (!jest.isMockFunction(mocked)) { 39 | const mock = jest.fn().mockImplementation(() => client); 40 | Object.assign(mock, real); 41 | traverse(AWS).set(split, mock); 42 | } 43 | 44 | return client; 45 | }; 46 | 47 | AWS.spyOn = (service, method) => { 48 | const client = clients.get(service); 49 | if (jest.isMockFunction(client[method])) 50 | throw new Error(`${service}.${method} is already mocked`); 51 | return jest.spyOn(client, method); 52 | }; 53 | 54 | AWS.spyOnPromise = (service, method, response = {}) => { 55 | const client = clients.get(service); 56 | if (jest.isMockFunction(client[method])) 57 | throw new Error(`${service}.${method} is already mocked`); 58 | 59 | return jest.spyOn(client, method).mockImplementation((params) => { 60 | const req = new _AWS.Request(client.service || client, method, params); 61 | req.promise = () => 62 | new Promise((resolve, reject) => 63 | process.nextTick(() => { 64 | if (response instanceof Error) return reject(response); 65 | resolve(response); 66 | }) 67 | ); 68 | return req; 69 | }); 70 | }; 71 | 72 | AWS.spyOnEachPage = (service, method, pages) => { 73 | if (!Array.isArray(pages)) 74 | throw new Error('to mock .eachPage(), you must provide an array of pages'); 75 | 76 | const client = clients.get(service); 77 | if (jest.isMockFunction(client[method])) 78 | throw new Error(`${service}.${method} is already mocked`); 79 | 80 | let i = 0; 81 | const sendPage = (callback) => { 82 | process.nextTick(() => { 83 | const page = pages[i]; 84 | if (!page) return callback(); 85 | i++; 86 | if (page instanceof Error) return callback(page); 87 | callback(null, page, () => sendPage(callback)); 88 | }); 89 | }; 90 | 91 | return jest.spyOn(client, method).mockImplementation((params) => { 92 | const req = new _AWS.Request(client.service || client, method, params); 93 | req.eachPage = jest.fn().mockImplementation(sendPage); 94 | return req; 95 | }); 96 | }; 97 | 98 | AWS.clearAllMocks = () => { 99 | const services = Object.keys(clients).filter((key) => key !== 'get'); 100 | services.forEach((service) => { 101 | delete clients[service]; 102 | const split = service.split('.'); 103 | const real = traverse(_AWS).get(split); 104 | traverse(AWS).set(split, real); 105 | }); 106 | }; 107 | 108 | module.exports = AWS; 109 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 Mapbox, Inc. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mapbox/aws-sdk-jest", 3 | "version": "0.0.4", 4 | "description": "", 5 | "main": "aws-sdk.js", 6 | "scripts": { 7 | "format-js": "prettier --write '**/!(package*).{js,json}'", 8 | "format-md": "kramer --format", 9 | "format": "npm run format-js && npm run format-md", 10 | "lint-js": "eslint . && prettier --check '**/!(package*).{js,json}'", 11 | "lint-md": "kramer", 12 | "lint": "npm run lint-js && npm run lint-md", 13 | "test": "jest --coverage && npm run lint" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/mapbox/aws-sdk-jest.git" 18 | }, 19 | "author": "Mapbox, Inc.", 20 | "license": "BSD-2-Clause", 21 | "bugs": { 22 | "url": "https://github.com/mapbox/aws-sdk-jest/issues" 23 | }, 24 | "homepage": "https://github.com/mapbox/aws-sdk-jest#readme", 25 | "devDependencies": { 26 | "@mapbox/eslint-config-mapbox": "^3.0.0", 27 | "@mapbox/kramer": "^1.0.0", 28 | "aws-sdk": "^2.720.0", 29 | "eslint": "^7.5.0", 30 | "eslint-config-prettier": "^6.11.0", 31 | "eslint-plugin-node": "^11.1.0", 32 | "husky": "^4.2.5", 33 | "jest": "^26.1.0", 34 | "jest-circus": "^26.1.0", 35 | "lint-staged": "^10.2.11", 36 | "prettier": "^2.0.5" 37 | }, 38 | "dependencies": { 39 | "traverse": "^0.6.6" 40 | }, 41 | "eslintConfig": { 42 | "extends": [ 43 | "@mapbox/eslint-config-mapbox", 44 | "prettier" 45 | ], 46 | "overrides": [ 47 | { 48 | "files": [ 49 | "aws-sdk.js", 50 | "test/*.test.js" 51 | ], 52 | "env": { 53 | "jest": true 54 | } 55 | } 56 | ] 57 | }, 58 | "lint-staged": { 59 | "*.js": [ 60 | "eslint", 61 | "prettier --write" 62 | ], 63 | "!(package*).json": [ 64 | "prettier --write" 65 | ], 66 | "*.md": [ 67 | "kramer", 68 | "kramer --format" 69 | ] 70 | }, 71 | "husky": { 72 | "hooks": { 73 | "pre-commit": "lint-staged" 74 | } 75 | }, 76 | "prettier": { 77 | "singleQuote": true, 78 | "trailingComma": "none" 79 | }, 80 | "jest": { 81 | "testEnvironment": "node", 82 | "testRunner": "jest-circus/runner", 83 | "clearMocks": true, 84 | "transform": {}, 85 | "coverageReporters": [ 86 | "text", 87 | "html" 88 | ], 89 | "coveragePathIgnorePatterns": [ 90 | "/node_modules/", 91 | "/test/" 92 | ] 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # @mapbox/aws-sdk-jest 2 | 3 | A generic Jest mock for the aws-sdk module. 4 | 5 | ## How to set it up 6 | 7 | 1. Install this module as a devDependency `npm install -D @mapbox/aws-sdk-jest`. 8 | 2. Add a file in your repository at `test/__mocks__/aws-sdk.js` 9 | 3. The file should look like this: 10 | 11 | ```js 12 | 'use strict'; 13 | 14 | module.exports = require('@mapbox/aws-sdk-jest'); 15 | ``` 16 | 17 | ## The mocking methods it provides 18 | 19 | **AWS.spyOn(service, method)** 20 | 21 | Returns a Jest mock function for an AWS SDK method call like `s3.getObject()`. You provide your own `.mockImplementation()` or `.mockReturnValue()` by setting it on the mock function in your test. 22 | 23 | **AWS.spyOnPromise(service, method, response)** 24 | 25 | Again, returns a Jest mock function for an AWS SDK method call like `s3.getObject()`. However, it anticipates that your code under test will use the `.promise()` method. **The `response` argument is optional**. 26 | 27 | - If you do not provide a `response`, the `.promise()` will resolve with an empty object. 28 | - If you provide an Error object as `response`, the `.promise()` will reject with the error. 29 | - If you provide any other thing as `response`, the `.promise()` will resolve with that thing. 30 | 31 | **AWS.spyOnEachPage(service, method, pages)** 32 | 33 | Also returns a Jest mock function for an AWS SDK method call that supports pagination, like `s3.listObjectsV2()`. This time, it anticipates that your code under test will use the `.eachPage()` method. 34 | 35 | **The `pages` argument is required**, and must be an array representing the pages that the code will observe during the test. If any of the pages are an Error object, then that error will be returned to the `.eachPage()` caller after sending non-error pages. 36 | 37 | **AWS.clearAllMocks()** 38 | 39 | This clears all your mocks and should be used in `afterEach()` functions. 40 | 41 | ## Examples 42 | 43 | Here is an example function that maybe you would like to test. 44 | 45 | ```js 46 | const getThing = async () => { 47 | const s3 = new AWS.S3({ region: 'us-east-1' }); 48 | return await s3.getObject({ Bucket: 'my', Key: 'thing' }).promise(); 49 | }; 50 | ``` 51 | 52 | Here is a set of tests for it. 53 | 54 | ```js 55 | 'use strict'; 56 | 57 | const AWS = require('aws-sdk'); 58 | 59 | describe('getting things', () => { 60 | afterEach(() => AWS.clearAllMocks()); 61 | 62 | it('returns the thing', async () => { 63 | const get = AWS.spyOn('S3', 'getObject').mockReturnValue({ 64 | promise: () => Promise.resolve('foo') 65 | }); 66 | 67 | const thing = await getThing(); 68 | expect(thing).toEqual('foo'); 69 | }); 70 | 71 | it('sets up an s3 client in the right region', async () => { 72 | AWS.spyOn('S3', 'getObject').mockReturnValue({ 73 | promise: () => Promise.resolve('foo') 74 | }); 75 | 76 | await getThing(); 77 | expect(AWS.S3).toHaveBeenCalledWith({ region: 'us-east-1' }); 78 | }); 79 | 80 | it('asks for the right thing', async () => { 81 | const get = AWS.spyOn('S3', 'getObject').mockReturnValue({ 82 | promise: () => Promise.resolve('foo') 83 | }); 84 | 85 | await getThing(); 86 | expect(get).toHaveBeenCalledWith({ Bucket: 'my', Key: 'thing' }); 87 | }); 88 | 89 | it('can mock .promise() directly', async () => { 90 | const get = AWS.spyOnPromise('S3', 'getObject', { Body: 'foo' }); 91 | const result = await getThing(); 92 | expect(result).toStrictEqual({ Body: 'foo' }); 93 | expect(get).toHaveBeenCalledWith({ Bucket: 'my', Key: 'thing' }); 94 | }); 95 | 96 | it('can handle mocked .promise() errors', async () => { 97 | const get = AWS.spyOnPromise('S3', 'getObject', new Error('foo')); 98 | await expect(() => getThing()).rejects.toThrow('foo'); 99 | }); 100 | }); 101 | ``` 102 | 103 | If your code uses `.eachPage()`, there's a way to mock that, too. Say you're testing this function: 104 | 105 | ```js 106 | const listThings = () => 107 | new Promise((resolve, reject) => { 108 | const s3 = new AWS.S3({ region: 'us-east-1' }); 109 | let things = []; 110 | s3.listObjectsV2({ Bucket: 'myBucket' }).eachPage((err, data, done) => { 111 | if (err) return reject(err); 112 | if (!data) return resolve(things); 113 | things = things.concat(data.Contents); 114 | done(); 115 | }); 116 | }); 117 | ``` 118 | 119 | You can mock the method call by providing a list of pages that should be returned. 120 | 121 | ```js 122 | 'use strict'; 123 | 124 | const AWS = require('aws-sdk'); 125 | 126 | describe('listing things', () => { 127 | afterEach(() => AWS.clearAllMocks()); 128 | 129 | it('can mock .eachPage directly', async () => { 130 | const list = AWS.spyOnEachPage('S3', 'listObjectsV2', [ 131 | { Contents: [1, 2, 3] }, 132 | { Contents: [4, 5, 6] } 133 | ]); 134 | 135 | const result = await listThings(); 136 | expect(result).toStrictEqual([1, 2, 3, 4, 5, 6]); 137 | expect(AWS.S3).toHaveBeenCalledWith({ region: 'us-east-1' }); 138 | expect(list).toHaveBeenCalledWith({ Bucket: 'myBucket' }); 139 | }); 140 | 141 | it('can mock .eachPage errors on any page', async () => { 142 | AWS.spyOnEachPage('S3', 'listObjectsV2', [ 143 | { Contents: [1, 2, 3] }, 144 | new Error('foo') 145 | ]); 146 | 147 | await expect(() => listThings()).rejects.toThrow('foo'); 148 | }); 149 | }); 150 | ``` 151 | 152 | ## Some notes 153 | 154 | - If you try to mock a method twice, you will get an error. 155 | - For nested clients like `AWS.DynamoDB.DocumentClient`, you can mock methods like this: `AWS.spyOn('DynamoDB.DocumentClient', 'get')`. 156 | - You should be familiar with the [AWS.Request][1] object, because if your code needs to set special expectations for `.promise()`, `.eachPage()`, or `.on()`, then you're going to have to use `AWS.spyOn()` and provide your own implementations for those Request methods. 157 | - If your application is using [`dyno`][2] ... don't! Use `AWS.DynamoDB.DocumentClient`, instead! But if you must, you'll need to mock the nested paths of the `aws-sdk` that `dyno` uses: 158 | ```js 159 | jest.mock('aws-sdk/lib/dynamodb/set', () => jest.fn()); 160 | jest.mock('aws-sdk/lib/dynamodb/converter', () => jest.fn()); 161 | ``` 162 | 163 | [1]: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html 164 | 165 | [2]: https://github.com/mapbox/dyno 166 | -------------------------------------------------------------------------------- /test/__mocks__/aws-sdk.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('../../aws-sdk'); 4 | -------------------------------------------------------------------------------- /test/mock.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AWS = require('aws-sdk'); 4 | const RealAWS = jest.requireActual('aws-sdk'); 5 | 6 | const underTest = async () => { 7 | const s3 = new AWS.S3({ region: 'ab-cdef-1' }); 8 | return await s3.getObject({ Bucket: 'my', Key: 'thing' }).promise(); 9 | }; 10 | 11 | const eachPager = () => 12 | new Promise((resolve, reject) => { 13 | const s3 = new AWS.S3({ region: 'ab-cdef-1' }); 14 | let things = []; 15 | s3.listObjectsV2({ Bucket: 'my' }).eachPage((err, data, done) => { 16 | if (err) return reject(err); 17 | if (!data) return resolve(things); 18 | things = things.concat(data.Contents); 19 | done(); 20 | }); 21 | }); 22 | 23 | const nested = async () => { 24 | const ddb = new AWS.DynamoDB.DocumentClient({ region: 'us-west-3' }); 25 | return await ddb.get({ Key: { key: 'value' } }).promise(); 26 | }; 27 | 28 | describe('stubbing', () => { 29 | afterEach(() => AWS.clearAllMocks()); 30 | 31 | it('stubs constructors and methods', async () => { 32 | const get = AWS.spyOn('S3', 'getObject').mockReturnValue({ 33 | promise: () => Promise.resolve('foo') 34 | }); 35 | 36 | await expect(underTest()).resolves.toEqual('foo'); 37 | expect(AWS.S3).toHaveBeenCalledWith({ region: 'ab-cdef-1' }); 38 | expect(get).toHaveBeenCalledWith({ Bucket: 'my', Key: 'thing' }); 39 | }); 40 | 41 | it('clears mocks', async () => { 42 | AWS.spyOn('S3', 'putObject'); 43 | 44 | const s3 = new AWS.S3(); 45 | expect(jest.isMockFunction(s3.putObject)).toEqual(true); 46 | 47 | AWS.clearAllMocks(); 48 | const after = new AWS.S3(); 49 | expect(jest.isMockFunction(after.putObject)).toEqual(false); 50 | expect(jest.isMockFunction(after.getObject)).toEqual(false); 51 | }); 52 | 53 | it('does not let you stub a method twice', () => { 54 | AWS.spyOn('S3', 'putObject'); 55 | 56 | expect(() => AWS.spyOn('S3', 'putObject')).toThrow( 57 | 'S3.putObject is already mocked' 58 | ); 59 | }); 60 | 61 | it('lets you make AWS clients that are not stubbed', () => { 62 | const s3 = new AWS.S3(); 63 | expect(jest.isMockFunction(s3.getObject)).toEqual(false); 64 | }); 65 | 66 | it('does not let you stub non-existent methods', () => { 67 | expect(() => AWS.spyOn('S3', 'FlyingSpaghettiMonster')).toThrow(); 68 | }); 69 | 70 | it('can mock .promise()', async () => { 71 | const response = { Body: 'foo' }; 72 | const get = AWS.spyOnPromise('S3', 'getObject', response); 73 | 74 | const result = await underTest(); 75 | expect(result).toEqual(response); 76 | expect(get).toHaveBeenCalledWith({ Bucket: 'my', Key: 'thing' }); 77 | }); 78 | 79 | it('can mock .promise() with no return value', async () => { 80 | AWS.spyOnPromise('S3', 'getObject'); 81 | const result = await underTest(); 82 | expect(result).toStrictEqual({}); 83 | }); 84 | 85 | it('can mock .promise() with error', async () => { 86 | AWS.spyOnPromise('S3', 'getObject', new Error('foo')); 87 | await expect(() => underTest()).rejects.toThrow('foo'); 88 | }); 89 | 90 | it('cannot double-mock .promise()', async () => { 91 | AWS.spyOnPromise('S3', 'getObject'); 92 | expect(() => AWS.spyOnPromise('S3', 'getObject')).toThrow( 93 | 'S3.getObject is already mocked' 94 | ); 95 | }); 96 | 97 | it('can mock .eachPage() with one page', async () => { 98 | const list = AWS.spyOnEachPage('S3', 'listObjectsV2', [ 99 | { Contents: [1, 2, 3] } 100 | ]); 101 | 102 | const result = await eachPager(); 103 | expect(result).toStrictEqual([1, 2, 3]); 104 | expect(list).toHaveBeenCalledWith({ Bucket: 'my' }); 105 | }); 106 | 107 | it('can mock .eachPage() with multiple pages', async () => { 108 | AWS.spyOnEachPage('S3', 'listObjectsV2', [ 109 | { Contents: [1, 2, 3] }, 110 | { Contents: [4, 5, 6] } 111 | ]); 112 | 113 | const result = await eachPager(); 114 | expect(result).toStrictEqual([1, 2, 3, 4, 5, 6]); 115 | }); 116 | 117 | it('can mock .eachPage() errors', async () => { 118 | AWS.spyOnEachPage('S3', 'listObjectsV2', [ 119 | { Contents: [1, 2, 3] }, 120 | new Error('foo') 121 | ]); 122 | 123 | await expect(() => eachPager()).rejects.toThrow('foo'); 124 | }); 125 | 126 | it('demands you provide pages to mock .eachPage()', async () => { 127 | expect(() => AWS.spyOnEachPage('S3', 'listObjectsV2')).toThrow( 128 | 'to mock .eachPage(), you must provide an array of pages' 129 | ); 130 | }); 131 | 132 | it('cannot double-mock .eachPage()', async () => { 133 | AWS.spyOnEachPage('S3', 'listObjectsV2', []); 134 | expect(() => AWS.spyOnEachPage('S3', 'listObjectsV2', [])).toThrow( 135 | 'S3.listObjectsV2 is already mocked' 136 | ); 137 | }); 138 | 139 | it('can mock and clear nested clients like DynamoDB.DocumentClient', async () => { 140 | const get = AWS.spyOnPromise('DynamoDB.DocumentClient', 'get', { 141 | key: 'value', 142 | data: 'stuff' 143 | }); 144 | const result = await nested(); 145 | expect(result).toStrictEqual({ key: 'value', data: 'stuff' }); 146 | expect(get).toHaveBeenCalledWith({ Key: { key: 'value' } }); 147 | 148 | expect(() => { 149 | new AWS.DynamoDB(); 150 | }).not.toThrow(); 151 | 152 | AWS.clearAllMocks(); 153 | const ddb = new AWS.DynamoDB.DocumentClient(); 154 | expect(jest.isMockFunction(ddb.get)).toEqual(false); 155 | expect(AWS['DynamoDB.DocumentClient']).toEqual(undefined); 156 | }); 157 | 158 | it('can mock both top-level and nested clients without breaking client props', async () => { 159 | expect(AWS.DynamoDB.DocumentClient).toBe(RealAWS.DynamoDB.DocumentClient); 160 | expect(AWS.DynamoDB.Converter).toBe(RealAWS.DynamoDB.Converter); 161 | 162 | const getItem = AWS.spyOnPromise('DynamoDB', 'getItem', { 163 | Item: { key: 'getItem' } 164 | }); 165 | const get = AWS.spyOnPromise('DynamoDB.DocumentClient', 'get', { 166 | Item: { key: 'get' } 167 | }); 168 | 169 | expect(jest.isMockFunction(AWS.DynamoDB)).toEqual(true); 170 | expect(jest.isMockFunction(AWS.DynamoDB.DocumentClient)).toEqual(true); 171 | expect(AWS.DynamoDB.Converter).toBe(RealAWS.DynamoDB.Converter); 172 | 173 | const ddb = new AWS.DynamoDB({ region: 'us-west-3' }); 174 | const ddbResult = await ddb.getItem({ Key: { key: 'getItem' } }).promise(); 175 | expect(ddbResult).toEqual({ Item: { key: 'getItem' } }); 176 | expect(getItem).toHaveBeenCalledTimes(1); 177 | expect(get).toHaveBeenCalledTimes(0); 178 | expect(getItem).toHaveBeenCalledWith({ Key: { key: 'getItem' } }); 179 | 180 | const docClient = new AWS.DynamoDB.DocumentClient({ region: 'us-west-3' }); 181 | const docClientResult = await docClient 182 | .get({ Key: { key: 'get' } }) 183 | .promise(); 184 | expect(docClientResult).toEqual({ Item: { key: 'get' } }); 185 | expect(getItem).toHaveBeenCalledTimes(1); 186 | expect(get).toHaveBeenCalledTimes(1); 187 | expect(get).toHaveBeenCalledWith({ Key: { key: 'get' } }); 188 | }); 189 | }); 190 | --------------------------------------------------------------------------------