NodeJS 如何为访问aws资源的函数编写单元测试?

5ssjco0h  于 2022-11-22  发布在  Node.js
关注(0)|答案(4)|浏览(108)

我 有 一 个 访问 多 个 aws 资源 的 函数 , 现在 需要 测试 这个 函数 , 但是 我 不 知道 如何 模拟 这些 资源 。
我 试 过 跟随 aws-sdk - mock 的 github , 但是 没有 得到 很多 。

function someData(event, configuration, callback) {

    // sts set-up
    var sts = new AWS.STS(configuration.STS_CONFIG);

    sts.assumeRole({
      DurationSeconds: 3600,
      RoleArn: process.env.CROSS_ACCOUNT_ROLE,
      RoleSessionName: configuration.ROLE_NAME
    }, function(err, data) {
      if (err) {
        // an error occurred
        console.log(err, err.stack);
      } else {
        // successful response

        // resolving static credential
        var creds = new AWS.Credentials({
          accessKeyId: data.Credentials.AccessKeyId,
          secretAccessKey: data.Credentials.SecretAccessKey,
          sessionToken: data.Credentials.SessionToken
        });

         // Query function
         var dynamodb = new AWS.DynamoDB({apiVersion: configuration.API_VERSION, credentials:  creds, region: configuration.REGION});
         var docClient = new AWS.DynamoDB.DocumentClient({apiVersion: configuration.API_VERSION, region: configuration.REGION, endpoint: configuration.DDB_ENDPOINT, service: dynamodb });

            // extract params
            var ID = event.queryStringParameters.Id;
            console.log('metrics of id ' + ID);

            var params = {
                TableName: configuration.TABLE_NAME,
                ProjectionExpression: configuration.PROJECTION_ATTR,
                KeyConditionExpression: '#ID = :ID',
                ExpressionAttributeNames: {
                    '#ID': configuration.ID
                },
                ExpressionAttributeValues: {
                    ':ID': ID
                }
            };

            queryDynamoDB(params, docClient).then((response) => {
                console.log('Params: ' + JSON.stringify(params));
                // if the query is Successful
                if( typeof(response[0]) !== 'undefined'){
                    response[0]['Steps'] = process.env.STEPS;
                    response[0]['PageName'] = process.env.STEPS_NAME;
                }
                console.log('The response you get', response);
                var success = {
                    statusCode: HTTP_RESPONSE_CONSTANTS.SUCCESS.statusCode,
                    body: JSON.stringify(response),
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    isBase64Encoded: false
                };
                return callback(null, success);
            }, (err) => {
                // return internal server error
                return callback(null, HTTP_RESPONSE_CONSTANTS.BAD_REQUEST);
            });
      }

    });

}

中 的 每 一 个
这 是 我 需要 测试 的 lambda 函数 , 这里 还 使用 了 一些 env 变量 。
现在 我 试 着 用 aws-sdk-mock 为 上面 的 函数 写 单元 测试 , 但是 我 仍然 不 知道 如何 去 做 。 任何 帮助 都 将 不胜 感激 。 下面 是 我 的 测试 代码

describe('test getMetrics', function() {

    var expectedOnInvalid = HTTP_RESPONSE_CONSTANTS.BAD_REQUEST;

    it('should assume role ', function(done){
        var event = {
          queryStringParameters : {
              Id: '123456'
          }
        };

        AWS.mock('STS', 'assumeRole', 'roleAssumed');
        AWS.restore('STS');
        AWS.mock('Credentials', 'credentials');
        AWS.restore('Credentials');
        AWS.mock('DynamoDB.DocumentClient', 'get', 'message');
        AWS.mock('DynamoDB', 'describeTable', 'message');
        AWS.restore('DynamoDB');
        AWS.restore('DynamoDB.DocumentClient');

        someData(event, configuration, (err, response) => {
            expect(response).to.deep.equal(expectedOnInvalid);
            done();
        });

    });

});

格式
出现 以下 错误 :

{ MultipleValidationErrors: There were 2 validation errors:
* MissingRequiredParameter: Missing required key 'RoleArn' in params
* MissingRequiredParameter: Missing required key 'RoleSessionName' in params

格式

vtwuwzda

vtwuwzda1#

尝试显式设置aws-sdk模块。

在顶层node_modules项目文件夹中不包含aws-sdk的项目结构将不会被正确模拟。例如,在嵌套的项目目录中安装aws-sdk。您可以通过使用setSDK()显式设置嵌套aws-sdk模块的路径来解决此问题。

const AWSMock = require('aws-sdk-mock');
import AWS = require('aws-sdk');
AWSMock.setSDKInstance(AWS);

如需了解更多详细信息,请访问:阅读aws-sdk-mock文档,他们已经解释得更好了。

lymgl2op

lymgl2op2#

我 强烈 不 同意@ttulka 的 回答 , 所以 我 决定 也 加上 我 自己 的 。
假设 你 在 Lambda 函数 中 收到 了 一 个 事件 , 你 很 可能 会 处理 这个 事件 , 然后 调用 其他 一些 服务 。 它 可能 是 对 S3 、 DynamoDB 、 SQS 、 SNS 、 Kinesis ... ... 你 能 想到 的 。 在 这 一 点 上 有 什么 要 Assert 的 呢 ?
正确 的 论点 !
考虑 以下 事件 :

{
   "data": "some-data",
   "user": "some-user",
   "additionalInfo": "additionalInfo"
}

中 的 每 一 个
现在 , 假设 您 要 调用 documentClient.put , 并且 要 确保 传递 的 参数 是 正确 的 。

delete event.additionalInfo

格式
对 不 对 ?
现在 , 您 可以 创建 一 个 单元 测试 来 * assert * 正确 的 参数 被 传递 到 documentClient.put , 这 意味 着 最终 的 对象 应该 如下 所 示 :

{
   "data": "some-data",
   "user": "some-user"
 }

格式
您 的 测试 必须 Assert documentClient.put 是 使用 深度 等于 上述 JSON 的 JSON 调用 的 。
如果 您 或 任何 其他 开发 人员 现在 出于 某种 原因 删除 了 delete event.additionalInfo 行 , 测试 将 开始 失败 。
这 是 非常 强大 的 ! 如果 你 确保 你 的 代码 按照 你 期望 的 方式 工作 , 你 基本 上 根本 不必 担心 创建 集成 测试 。
现在 , 如果 SQS 消费 者 Lambda 期望 消息 的 主体 包含 某 个 字段 , 那么 生产 者 Lambda 应该 总是 处理 它 , 以 确保 正确 的 参数 被 持久 保存 在 Queue 中 。 我 想 现在 你 已经 明白 了 , 对 吧 ?
我 总是 告诉 我 的 同事 , 如果 我们 能 创建 * * * 适当 的 * * * 单元 测试 , 我们 应该 可以 在 95% 的 情况 下 进行 , 而 不 需要 集成 测试 。 当然 , 两者 都 有 更 好 , 但是 考虑 到 创建 集成 测试 ( 如 设置 环境 、 凭据 , 有时 甚至 是 不同 的 帐户 ) 所 花费 的 时间 ,不 值得 。 但 这 只是 我 的 看法 。 欢迎 你 和@ttulka 提出 不同 意见 。
现在 , 回到 你 的 问题 :
您 可以 使用 Sinon 来 模拟 和 Assert Lambda 函数 中 的 参数 。 如果 您 需要 模拟 第 三方 服务 ( 如 DynamoDB 、 SQS 等 ) , 您 可以 创建 一 个 模拟 对象 , 并 使用 Rewire 在 测试 文件 中 替换 它 。 这 通常 是 我 所 走 的 路 , 到 目前 为止 一直 很 好 。

1sbrub3j

1sbrub3j3#

我认为单元测试是一种检查您的域(业务)规则是否得到满足的方法。
只要您的Lambda只包含AWS服务的集成,为它编写单元测试就没有多大意义。
模拟所有资源意味着,您的测试将只测试这些模拟之间的通信-这样的测试没有任何价值。
外部资源是指输入/输出,这是集成测试所关注的。
编写集成测试并将其作为集成管道的一部分针对真实的部署的资源运行。

qeeaahzv

qeeaahzv4#

这就是我们在nodeJ中模拟STS的方法。

import { STS } from 'aws-sdk';

export default class GetCredential {
  constructor(public sts: STS) { }

  public async getCredentials(role: string) {
    this.log.info('Retrieving credential...', { role });

    const apiRole = await this.sts
      .assumeRole({
        RoleArn: role,
        RoleSessionName: 'test-api',
      })
      .promise();
    if (!apiRole?.Credentials) {
      throw new Error(`Credentials for ${role} could not be retrieved`);
    }

    return apiRole.Credentials;
  }
}

模拟上述功能

import { STS } from 'aws-sdk';
import CredentialRepository from './GetCredential'; 

const sts = new STS();    
let testService: GetCredential;

beforeEach(() => {
  testService = new GetCredential(sts);
});

describe('Given getCredentials has been called', () => {
  it('The method returns a credential', async () => {
    const credential = {
      AccessKeyId: 'AccessKeyId',
      SecretAccessKey: 'SecretAccessKey',
      SessionToken: 'SessionToken'
    };

    const mockGetCredentials = jest.fn().mockReturnValue({
      promise: () => Promise.resolve({ Credentials: credential }),
    });
    testService.sts.assumeRole = mockGetCredentials;
    const result = await testService.getCredentials('fakeRole');
    expect(result).toEqual(credential);
  });
});

相关问题