如何在Jest中为无服务器Nodejs Lambda模拟AWS DynamoDB?

时间:2020-10-27 23:10:43

标签: node.js aws-lambda jestjs mocking serverless-framework

我写了一个lambda,如下。

handler.js

const aws = require('aws-sdk');
const dynamoDb = new aws.DynamoDB.DocumentClient();

const testHandler = async event => {
  // some code
  // ...
  const user = await getUser(userId)
  // ...
  // some code
}

const promisify = foo => new Promise((resolve, reject) => {
  foo((error, result) => {
    if (error) {
      reject(error)
    } else {
      resolve(result)
    }
  })
})

const getUser = (userId) => promisify(callback =>
  dynamoDb.get({
    TableName: 'test-table',
    Key: {
      "PK": `${userId}`,
      "SK": `${userId}`
    }
  }, callback))
  .then((user) => {
    console.log(`Retrieved user: ${userId}`)
    return user
  })


module.exports = {
  testHandler: testHandler,
  getUser: getUser
}

我想编写一个用于测试getUser函数的单元测试,所以我尝试了以下方法。

handler.test.js

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

const dynamoDbGetParameterPromise = jest.fn().mockReturnValue({
  promise: jest.fn().mockResolvedValue({
    PK: 'userId-123', SK: 'userId-123'
  })
})

AWS.DynamoDB.DocumentClient = jest.fn().mockImplementation(() => ({
  get: dynamoDbGetParameterPromise
}))


describe('test getUser', () => {

  beforeEach(() => {
    jest.resetModules()
  });

  test('get user success', async () => {
    const user = { PK: 'userId-123', SK: 'userId-123' };
    const result = await handler.getUser(userId);
    expect(result).toEqual(user);
  });
});

错误如下。

ConfigError: Missing region in config

      105 |
      106 | const getUser = (userId) => promisify(callback =>
    > 107 |   dynamoDb.get({
          |            ^
      108 |     TableName: 'test-table',
      109 |     Key: {
      110 |       "PK": 'userId-123',

测试似乎仍然在dynamoDb中使用handler.js,而不是在测试中进行模拟。

关于如何正确连接模拟程序以测试功能的任何想法? 预先感谢!

2 个答案:

答案 0 :(得分:1)

您可以使用jest.mock(moduleName, factory, options)手动模拟aws-sdk模块。

例如

handler.js

const aws = require('aws-sdk');
const dynamoDb = new aws.DynamoDB.DocumentClient();

const promisify = (foo) =>
  new Promise((resolve, reject) => {
    foo((error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });

const getUser = (userId) =>
  promisify((callback) =>
    dynamoDb.get(
      {
        TableName: 'test-table',
        Key: {
          PK: `${userId}`,
          SK: `${userId}`,
        },
      },
      callback,
    ),
  ).then((user) => {
    console.log(`Retrieved user: ${userId}`);
    return user;
  });

module.exports = { getUser };

handler.test.js

const aws = require('aws-sdk');
const { getUser } = require('./handler');

jest.mock('aws-sdk', () => {
  const mDocumentClient = { get: jest.fn() };
  const mDynamoDB = { DocumentClient: jest.fn(() => mDocumentClient) };
  return { DynamoDB: mDynamoDB };
});
const mDynamoDb = new aws.DynamoDB.DocumentClient();

describe('64564233', () => {
  afterAll(() => {
    jest.resetAllMocks();
  });
  it('should get user', async () => {
    const mResult = { name: 'teresa teng' };
    mDynamoDb.get.mockImplementationOnce((_, callback) => callback(null, mResult));
    const actual = await getUser(1);
    expect(actual).toEqual({ name: 'teresa teng' });
    expect(mDynamoDb.get).toBeCalledWith(
      {
        TableName: 'test-table',
        Key: {
          PK: '1',
          SK: '1',
        },
      },
      expect.any(Function),
    );
  });

  it('should handler error', async () => {
    const mError = new Error('network');
    mDynamoDb.get.mockImplementationOnce((_, callback) => callback(mError));
    await expect(getUser(1)).rejects.toThrowError('network');
    expect(mDynamoDb.get).toBeCalledWith(
      {
        TableName: 'test-table',
        Key: {
          PK: '1',
          SK: '1',
        },
      },
      expect.any(Function),
    );
  });
});

单元测试结果:

 PASS  src/stackoverflow/64564233/handler.test.js (14.929s)
  64564233
    ✓ should get user (23ms)
    ✓ should handler error (3ms)

  console.log src/stackoverflow/64564233/handler.js:433
    Retrieved user: 1

------------|----------|----------|----------|----------|-------------------|
File        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files   |      100 |      100 |      100 |      100 |                   |
 handler.js |      100 |      100 |      100 |      100 |                   |
------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        17.435s

源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/64564233

答案 1 :(得分:1)

您可以通过添加来使用jest的自动模拟功能

jest.mock("aws-sdk");

,然后AWS.DynamoDB.DocumentClient将是一个模拟类,因此您可以模拟它的实现。并且由于我们希望它的get方法成为一个接受任何东西作为第一个参数的函数(因为我们不会在模拟实现中对其进行任何处理)和一个我们希望它被{{1 }}和null,我们可以这样模拟:

user