4

我有一个正在访问多个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
4

3 回答 3

1

我强烈不同意@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在您的测试文件中替换它。这通常是我骑的路,到目前为止它一直很棒。

于 2019-03-26T08:46:04.130 回答
1

尝试aws-sdk明确设置模块。
不包含aws-sdk顶级 node_modules 项目文件夹的项目结构将不会被正确模拟。这方面的一个例子是aws-sdk在嵌套的项目目录中安装。aws-sdk您可以通过使用 . 显式设置嵌套模块的路径来解决此问题setSDK()

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

有关这方面的更多详细信息:阅读 aws-sdk-mock 文档,他们对此进行了更好的解释。

于 2019-04-09T06:30:21.530 回答
0

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

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

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

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

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

于 2019-03-26T07:05:44.340 回答