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

Con*_*nan 9 mocking node.js jestjs aws-lambda serverless-framework

我写了一个 lambda 如下。

处理程序.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
}
Run Code Online (Sandbox Code Playgroud)

我想编写一个单元测试来测试该getUser功能,所以我尝试了以下方法。

处理程序.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);
  });
});

Run Code Online (Sandbox Code Playgroud)

错误如下。

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',
Run Code Online (Sandbox Code Playgroud)

看来测试仍然使用dynamoDbin thehandler.js而不是测试中的嘲笑。

关于如何正确连接模拟来测试功能有什么想法吗?提前致谢!

Ten*_*eff 7

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

jest.mock("aws-sdk");
Run Code Online (Sandbox Code Playgroud)

然后AWS.DynamoDB.DocumentClient将是一个模拟类,因此您将能够模拟它的实现。因为我们希望它的 get 方法成为一个接受任何东西作为第一个参数的函数(因为我们不会在模拟实现中对它做任何事情)和一个我们期望它被调用的回调,null我们user可以像这样嘲笑它:

AWS.DynamoDB.DocumentClient.prototype.get.mockImplementation((_, cb) => {
  cb(null, user);
});
Run Code Online (Sandbox Code Playgroud)


sli*_*wp2 6

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

\n

例如

\n

handler.js:

\n
const aws = require(\'aws-sdk\');\nconst dynamoDb = new aws.DynamoDB.DocumentClient();\n\nconst promisify = (foo) =>\n  new Promise((resolve, reject) => {\n    foo((error, result) => {\n      if (error) {\n        reject(error);\n      } else {\n        resolve(result);\n      }\n    });\n  });\n\nconst getUser = (userId) =>\n  promisify((callback) =>\n    dynamoDb.get(\n      {\n        TableName: \'test-table\',\n        Key: {\n          PK: `${userId}`,\n          SK: `${userId}`,\n        },\n      },\n      callback,\n    ),\n  ).then((user) => {\n    console.log(`Retrieved user: ${userId}`);\n    return user;\n  });\n\nmodule.exports = { getUser };\n
Run Code Online (Sandbox Code Playgroud)\n

handler.test.js:

\n
const aws = require(\'aws-sdk\');\nconst { getUser } = require(\'./handler\');\n\njest.mock(\'aws-sdk\', () => {\n  const mDocumentClient = { get: jest.fn() };\n  const mDynamoDB = { DocumentClient: jest.fn(() => mDocumentClient) };\n  return { DynamoDB: mDynamoDB };\n});\nconst mDynamoDb = new aws.DynamoDB.DocumentClient();\n\ndescribe(\'64564233\', () => {\n  afterAll(() => {\n    jest.resetAllMocks();\n  });\n  it(\'should get user\', async () => {\n    const mResult = { name: \'teresa teng\' };\n    mDynamoDb.get.mockImplementationOnce((_, callback) => callback(null, mResult));\n    const actual = await getUser(1);\n    expect(actual).toEqual({ name: \'teresa teng\' });\n    expect(mDynamoDb.get).toBeCalledWith(\n      {\n        TableName: \'test-table\',\n        Key: {\n          PK: \'1\',\n          SK: \'1\',\n        },\n      },\n      expect.any(Function),\n    );\n  });\n\n  it(\'should handler error\', async () => {\n    const mError = new Error(\'network\');\n    mDynamoDb.get.mockImplementationOnce((_, callback) => callback(mError));\n    await expect(getUser(1)).rejects.toThrowError(\'network\');\n    expect(mDynamoDb.get).toBeCalledWith(\n      {\n        TableName: \'test-table\',\n        Key: {\n          PK: \'1\',\n          SK: \'1\',\n        },\n      },\n      expect.any(Function),\n    );\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

单元测试结果:

\n
 PASS  src/stackoverflow/64564233/handler.test.js (14.929s)\n  64564233\n    \xe2\x9c\x93 should get user (23ms)\n    \xe2\x9c\x93 should handler error (3ms)\n\n  console.log src/stackoverflow/64564233/handler.js:433\n    Retrieved user: 1\n\n------------|----------|----------|----------|----------|-------------------|\nFile        |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |\n------------|----------|----------|----------|----------|-------------------|\nAll files   |      100 |      100 |      100 |      100 |                   |\n handler.js |      100 |      100 |      100 |      100 |                   |\n------------|----------|----------|----------|----------|-------------------|\nTest Suites: 1 passed, 1 total\nTests:       2 passed, 2 total\nSnapshots:   0 total\nTime:        17.435s\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n