Как написать модульный тест для функции, которая обращается к ресурсам aws?

У меня есть функция, которая обращается к нескольким aws ресурсам, и теперь мне нужно протестировать эту функцию, но я не знаю, как имитировать эти ресурсы.

Я пробовал следовать github aws-sdk-mock, но ничего не добился.

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

person ankuselfie    schedule 26.03.2019    source источник


Ответы (3)


Попробуйте явно установить aws-sdk module.
Структуры проекта, которые не включают 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, у них есть объяснил это даже лучше.

person Atul Kumar    schedule 09.04.2019

Я категорически не согласен с ответом @ttulka, поэтому решил добавить и свой собственный.

Если вы получили событие в своей лямбда-функции, очень вероятно, что вы обработаете событие, а затем вызовете какую-то другую службу. Это может быть звонок в 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, тесты начнут давать сбой.

И это очень мощно! Если вы убедитесь, что ваш код работает так, как вы ожидаете, вам вообще не нужно беспокоиться о создании интеграционных тестов.

Теперь, если Lambda-потребитель SQS ожидает, что тело сообщения будет содержать какое-то поле, производитель Lambda всегда должен позаботиться об этом, чтобы убедиться, что правильные аргументы сохраняются в очереди. Думаю, вы уже поняли идею, верно?

Я всегда говорю своим коллегам, что если мы сможем создать правильные модульные тесты, мы сможем пройти в 95% случаев, исключив интеграционные тесты. Конечно, лучше иметь и то, и другое, но с учетом количества времени, затрачиваемого на создание интеграционных тестов, таких как настройка сред, учетных данных, а иногда и разных учетных записей, этого не стоит. Но это только мое мнение. И вы, и @ttulka можете не согласиться.

Теперь вернемся к вашему вопросу:

Вы можете использовать Sinon для имитации и утверждения аргументов в ваших лямбда-функциях. Если вам нужно имитировать стороннюю службу (например, DynamoDB, SQS и т. Д.), Вы можете создать фиктивный объект и заменить его в тестируемом файле с помощью Перепрограммировать. Обычно я езжу по этой дороге, и до сих пор она была отличной.

person Thales Minussi    schedule 26.03.2019
comment
Эй, спасибо, что потратили на это время. Можете ли вы предоставить образец кода для указанной выше проблемы? - person ankuselfie; 27.03.2019

Я рассматриваю модульное тестирование как способ проверить, соблюдаются ли правила вашего домена (бизнеса).

Поскольку ваша Lambda содержит только интеграцию сервисов AWS, нет смысла писать для нее модульный тест.

Чтобы смоделировать все ресурсы, ваш тест будет тестировать только связь между этими макетами - такой тест не имеет значения.

Внешние ресурсы означают ввод / вывод, это то, на чем фокусируется интеграционное тестирование.

Напишите интеграционные тесты и запускайте их как часть вашего конвейера интеграции с реальными развернутыми ресурсами.

person ttulka    schedule 26.03.2019