Jest mock aws-sdk ReferenceError: Cannot access before initialization

10,606

Here is the unit test solution:

index.js:

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

const dynamodb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
const toRecord = (item) => ({
  id: item.id.S,
  name: item.name.S,
});

const findById = (id) =>
  dynamodb
    .getItem({
      TableName: 'table-name',
      Key: {
        id: { S: id },
      },
    })
    .promise()
    .then((result) => toRecord(result.Item))
    .catch((error) => console.log(error));

module.exports = { findById };

index.test.js:

const { findById } = require('./');
const AWS = require('aws-sdk');

jest.mock('aws-sdk', () => {
  const mDynamoDB = { getItem: jest.fn().mockReturnThis(), promise: jest.fn() };
  return { DynamoDB: jest.fn(() => mDynamoDB) };
});

describe('61157392', () => {
  let dynamodb;
  beforeAll(() => {
    dynamodb = new AWS.DynamoDB();
  });
  afterAll(() => {
    jest.resetAllMocks();
  });
  it('should pass', async () => {
    dynamodb.getItem().promise.mockResolvedValueOnce({
      Item: {
        id: { S: '1' },
        name: { S: 'a' },
      },
    });
    const actual = await findById('1');
    expect(actual).toEqual({ id: '1', name: 'a' });
    expect(dynamodb.getItem).toBeCalledWith({
      TableName: 'table-name',
      Key: {
        id: { S: '1' },
      },
    });
    expect(dynamodb.getItem().promise).toBeCalledTimes(1);
  });
});

unit test results with 100% coverage:

 PASS  stackoverflow/61157392/index.test.js (8.216s)
  61157392
    ✓ should pass (4ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   88.89 |      100 |      75 |    87.5 |                   
 index.js |   88.89 |      100 |      75 |    87.5 | 19                
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        9.559s

source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/61157392

Share:
10,606
grigori
Author by

grigori

Updated on June 24, 2022

Comments

  • grigori
    grigori almost 2 years

    Jest 25.3.0

    I am trying to mock DynamoDB dependency in my unit tests as follows:

    const { findById } = require('./mymodule');
    
    const mockDynamoDB = { getItem: jest.fn() };
    jest.mock('aws-sdk', () => ({
      DynamoDB: jest.fn(() => mockDynamoDB)
    }));
    
    describe('.', () => {
      it('..', () => {
        findById('test');
        expect(mockDynamoDB.getItem).toBeCalledWith({
          TableName: 'table-name',
          Key: {
            id: { S: 'test' }
          }
        });
      });
    });
    

    Unfortunately, when I do that, I get the following error:

    ReferenceError: Cannot access 'mockDynamoDB' before initialization

    Strangely, if I do this, I can avoid the ReferenceError:

    const mockGetItem = { promise: jest.fn() };
    jest.mock('aws-sdk', () => ({
      DynamoDB: jest.fn(() => ({
        getItem: jest.fn(() => mockGetItem)
      })
    }));
    

    but this doesn't suit my test, as I can't validate the params passed to the getItem function.

    The actual code under test is fairly simple, it looks something like this:

    const AWS = require('aws-sdk');
    
    const dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
    
    const toRecord = (item) => ({
      id: item.id.S,
      name: item.name.S
    });
    
    const findById = (id) => (
      dynamodb.getItem({
        TableName: 'table-name',
        Key: {
          id: { S: id }
        }
      }).promise()
        .then(result => toRecord(result.Item))
        .catch(error => console.log(error)
    );
    
    module.exports = {
      findById
    }
    

    If anyone has seen this before, or can shed some light on why the first example fails while the second works, it would really help me out. Thank you.

  • grigori
    grigori about 4 years
    Hi @slideshowp2, this looks great but I've hit an issue because in my code I've got something like this dynamodb.getItem(params).promise().then(onSuccess).catch(onE‌​rror). Using your approach, I'm getting TypeError: Cannot read property 'then' of undefined. (I've updated my example above, just to be clearer). Thanks for your help.
  • slideshowp2
    slideshowp2 about 4 years
    @grigori Updated answer based on your update. It works fine.
  • grigori
    grigori about 4 years
    you're right, I had a typo. Thanks a lot, this helped heaps!
  • amr ras
    amr ras almost 3 years
    In other cases jest will complain that DynamoDB should be mocked with a function prefixed with 'mock'. So, this doesn't seem like a legit solution. Jest just seems buggy. I've done this in one file and it runs, then in another file it complains.