如何为正在访问AWS资源的功能编写单元测试?

时间:2019-03-26 05:56:49

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

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

3 个答案:

答案 0 :(得分: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应该始终注意确保其正确的参数被保留在队列中。我想现在您已经知道了,对吧?

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

现在,回到您的问题:

您可以使用Sinon在Lambda函数中模拟和声明参数。如果您需要模拟第三方服务(例如DynamoDB,SQS等),则可以创建一个模拟对象,并使用Rewire将其替换为测试对象。通常这是我骑的路,到目前为止,一切都很棒。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

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

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

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

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