简体   繁体   中英

TypeError: AWS.DynamoDB.DocumentClient is not a constructor when testing with Jest

I am running jest tests to test a dynamodb.js file and a create.js file that uses the dynamodb.js file. The create.js module is generic and can insert into any tables by having the param object constructed and passed into it. However, I have been getting the error below and I need help with this.

TypeError: AWS.DynamoDB.DocumentClient is not a constructor

__mock__ folder

const getMock = jest.fn().mockImplementation(() => {
  return {
    promise() {
      return Promise.resolve({});
    }
  };
});

const putMock = jest.fn().mockImplementation(() => {
  return {
    promise() {
      return Promise.resolve({});
    }
  };
});

// eslint-disable-next-line func-names
function DynamoDB() {
  return {
    DocumentClient: jest.fn(() => ({
      get: getMock,
      put: putMock
    }))
  };
}

const AWS = { DynamoDB, getMock, putMock };
module.exports = AWS;

dynamodb.js

const AWS = require('aws-sdk');
const http = require('http');
const https = require('https');
const url = require('url');

module.exports = endpoint => {
  const { protocol } = url.parse(endpoint || '');

  const agentConfig = {
    keepAlive: true,
    keepAliveMsecs: 20000
  };

  const httpOptions =
    protocol === 'http:' ? { agent: new http.Agent(agentConfig) } : { agent: new https.Agent(agentConfig) };

  const db = new AWS.DynamoDB({
    endpoint,
    httpOptions
  });

  const docClient = new AWS.DynamoDB.DocumentClient({
    service: db
  });

  return {
    docClient,
    db
  };
};

dynamodb.spec.js

 
const AWS = require('aws-sdk');
const dynamodb = require('../../../src/dynamodb');

describe('dynamodb.js', () => {
  beforeEach(() => {
    // jest.resetModules();
  });

  test('calls generic-dynamodb-lib dynamodb', async () => {
    dynamodb('http://localhost:8001');

    expect(AWS.DynamoDB).toHaveBeenCalled();
    expect(AWS.DynamoDB.DocumentClient).toHaveBeenCalled();
  });
});

create.js

// Imports here

const create = async (log, docClient, table, tableRecord) => {
  try {
    await docClient.put({ TableName: table, Item: tableRecord }).promise();
  } catch (error) {
    log.error({ message: 'DynamoDB error', ...error });
    throw Error.internal();
  }

  return tableRecord;
};

module.exports = create;

I have also tried replacing the manual mock in mock with a doMock block but still continued getting the same error above. Once I get past this, how do I test create.js considering that docClient.js is being passed into the function? Thank you very much.

DocumentClient is supposed to be static property while it was mocked to be instance property.

It should be:

const DynamoDB = jest.fn().mockReturnValue({});
DynamoDB.DocumentClient = jest.fn().mockReturnValue({
  get: getMock,
  put: putMock
});

Thank you very much for your responses. I had already found a way to solve the problem before seeing the response here.

I did not need place any mocks in the __mock__ directory eventually.

Please see the tests that I came up with:

create.spec.js

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

const dynamodb = require('../../../src/dynamodb');

const create = require('../../../src/create');

describe('create.js', () => {
  beforeEach(() => {
    jest.resetModules();
  });

  test('calls DocumentClient put with a successful outcome', async () => {
    const log = { error: jest.fn() };

    const fakePut = jest.fn().mockImplementation(() => {
      return {
        promise() {
          return Promise.resolve({});
        }
      };
    });

    AWS.DynamoDB.DocumentClient = jest.fn(() => ({
      put: fakePut
    }));

    const document = {
      brands: ['visa', 'mc', 'amex', 'maestro', 'diners', 'discover', 'jcb']
    };

    const { docClient } = dynamodb('https://localhost:8001');
    await create(log, docClient, 'a-table-name', {
      countryCode: 'US',
      merchantAccount: 'MerchantAccountUS',
      expireAt: 1593814944,
      document
    });

    expect(create).toEqual(expect.any(Function));
    expect(fakePut).toHaveBeenCalled();
    expect(fakePut).toHaveBeenCalledWith({
      TableName: 'a-table-name',
      Item: {
        countryCode: 'US',
        merchantAccount: 'MerchantAccountUS',
        expireAt: 1593814944,
        document
      }
    });
  });

  test('calls DocumentClient put with unsuccessful outcome', async () => {
    const log = { error: jest.fn() };

    const fakePut = jest.fn().mockImplementation(() => {
      throw Error.internal();
    });

    AWS.DynamoDB.DocumentClient = jest.fn(() => ({
      put: fakePut
    }));

    const document = {
      brands: ['visa', 'mc', 'amex', 'maestro', 'diners', 'discover', 'jcb']
    };

    const { docClient } = dynamodb('https://localhost:8001');
    let thrownError;

    try {
      await create(log, docClient, 'a-table-name', {
        countryCode: 'US',
        merchantAccount: 'MerchantAccountUS',
        expireAt: 1593814944,
        document
      });
    } catch (e) {
      thrownError = e;
    }

    expect(create).toEqual(expect.any(Function));
    expect(fakePut).toHaveBeenCalled();
    expect(fakePut).toHaveBeenCalledWith({
      TableName: 'a-table-name',
      Item: {
        countryCode: 'US',
        merchantAccount: 'MerchantAccountUS',
        expireAt: 1593814944,
        document
      }
    });
    expect(thrownError).toEqual(Error.internal());
  });
});

dynamodb.spec.js

const AWS = require('aws-sdk');
const http = require('http');
const https = require('https');
const url = require('url');
const dynamodb = require('../../../src/dynamodb');

const fakeFunction = jest.fn().mockImplementation(() => {});

const FakeDynamoDB = jest.fn(() => ({
  DocumentClient: fakeFunction
}));
AWS.DynamoDB = FakeDynamoDB;

const fakeGet = fakeFunction;
const fakePut = fakeFunction;

const FakeDocumentClient = jest.fn(() => ({
  get: fakeGet,
  put: fakePut
}));
AWS.DynamoDB.DocumentClient = FakeDocumentClient;

describe('dynamodb.js', () => {
  beforeEach(() => {
    jest.resetModules();
  });

  test('calls DynamoDB and DocumentClient constructors with http protocol and with endpoint present', () => {
    const fakeParse = jest.fn().mockImplementation(() => 'http');
    url.parse = fakeParse;

    const fakeHttpAgent = jest.fn().mockImplementation(() => {});
    http.Agent = fakeHttpAgent;

    dynamodb('http://localhost:8001');

    expect(FakeDynamoDB).toHaveBeenCalled();
    expect(FakeDocumentClient).toHaveBeenCalled();
  });

  test('calls DynamoDB and DocumentClient constructors with https protocol and with endpoint present', () => {
    const fakeParse = jest.fn().mockImplementation(() => 'https');
    url.parse = fakeParse;

    const fakeHttpsAgent = jest.fn().mockImplementation(() => {});
    https.Agent = fakeHttpsAgent;

    dynamodb('https://localhost:8001');

    expect(FakeDynamoDB).toHaveBeenCalled();
    expect(FakeDocumentClient).toHaveBeenCalled();
  });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM