【问题标题】:How to write unit test for the function which is accessing aws resources?如何为访问 aws 资源的函数编写单元测试?
【发布时间】:2019-03-26 05:56:48
【问题描述】:

我有一个函数正在访问多个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 函数,这里也使用了一些环境变量。

现在我尝试使用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

【问题讨论】:

    标签: node.js amazon-web-services mocha.js chai aws-sdk-mock


    【解决方案1】:

    尝试明确设置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);
    

    有关此的更多详细信息:Read aws-sdk-mock documentation,他们对此进行了更好的解释。

    【讨论】:

      【解决方案2】:

      我非常不同意@ttulka 的回答,所以我决定也添加我自己的。

      假设您在 Lambda 函数中收到了一个事件,您很可能会处理该事件,然后调用其他一些服务。它可能是对 S3、DynamoDB、SQS、SNS、Kinesis 的调用......你可以说它。此时有什么要断言的?

      正确的论点!

      考虑以下事件:

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

      现在假设您想调用documentClient.put,并且您想确保您传递的参数是正确的。假设您不希望保留 additionalInfo 属性,因此,在您的代码中的某处,您可以使用它来摆脱此属性

      delete event.additionalInfo
      

      对吗?

      您现在可以创建一个单元测试以断言正确的参数已传递到documentClient.put,这意味着最终对象应如下所示:

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

      您的测试必须断言 documentClient.put 是使用深度等于上述 JSON 的 JSON 调用的。

      如果您或任何其他开发人员现在出于某种原因删除了 delete event.additionalInfo 行,测试将开始失败。

      这是非常强大的!如果您确保您的代码以您期望的方式运行,您基本上不必担心创建集成测试。

      现在,如果 SQS 消费者 Lambda 期望消息的正文包含某些字段,则生产者 Lambda 应该始终注意它,以确保正确的参数保留在队列中。我想你现在明白了吧?

      我总是告诉我的同事,如果我们可以创建适当的单元测试,我们应该可以在 95% 的情况下进行测试,而忽略集成测试。当然最好同时拥有两者,但考虑到创建集成测试(例如设置环境、凭据,有时甚至是不同的帐户)所花费的时间,是不值得的。但那只是我的个人意见。非常欢迎您和 @ttulka 不同意。

      现在,回到你的问题:

      您可以使用Sinon 在您的 Lambda 函数中模拟和断言参数。如果您需要模拟第 3 方服务(如 DynamoDB、SQS 等),您可以创建一个模拟对象并使用 Rewire 在您的测试文件中替换它。这通常是我骑的路,到目前为止它一直很棒。

      【讨论】:

      • 嘿,感谢您投入时间。能否提供上述问题的代码示例?
      【解决方案3】:

      我将单元测试视为检查您的域(业务)规则是否得到满足的一种方式。

      就您的 Lambda 仅包含 AWS 服务的集成而言,为其编写单元测试没有多大意义。

      要模拟所有资源意味着,您的测试将仅测试这些模拟之间的通信 - 这样的测试没有任何价值。

      外部资源意味着输入/输出,这是集成测试的重点。

      编写集成测试并将它们作为集成管道的一部分针对实际部署的资源运行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-21
        • 2017-05-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-14
        • 1970-01-01
        • 2017-02-15
        相关资源
        最近更新 更多