Мокирующие вызовы, сделанные в S3 в функции AWS Lambda с использованием Jest - PullRequest
0 голосов
/ 07 мая 2020

Я пытаюсь имитировать вызов putObject в локальной Node.JS AWS лямбда-функции с помощью Jest, но по какой-то причине я продолжаю получать 0 вызовов в ожидании.

Вот мой main fun c (index. js):

 const S3 = require("aws-sdk/clients/s3");
 const s3 = new S3();

 exports.handler = async (event) => {

   putFunction = async (params, callback) => {
     await s3.putObject(params, callback).promise();
   };

 const params = {
     Bucket: "some value",
     Key: "some key value",
     ContentType: "application/json",
     Body: "some body value",
   };

   const callback = {
     function(err, data) {
       console.log(JSON.stringify(err) + " " + JSON.stringify(data));
     },
   };
   putFunction(params, callback);
 }

Я пробовал добавить asyn c в свою тестовую функцию, так как я думал, что это асинхронная проблема, но, похоже, я все еще получение той же ошибки. Вот мой тестовый код (index.test. js):

 let myHandler = require("../../src/lambda/index");
 const mockedPutObject = jest.fn();
 jest.mock("aws-sdk/clients/s3", () => {
   return class S3 {
     putObject(params, cb) {
       mockedPutObject(params, cb);
     }
   };
 });

 it("has to mock s3#putObject", () => {
   const params = {
     Bucket: "test1",
     Key: "test2",
     ContentType: "application/json",
     Body: "test3",
   };

   const callback = {
     function(err, data) {
       console.log(JSON.stringify(err) + " " + JSON.stringify(data));
},
};
   const putFunc = myHandler.handler.putFunction;
   putFunc;
   expect(mockedPutObject).toHaveBeenCalledWith(params, callback);
 });

Любая помощь будет отличной.

1 Ответ

5 голосов
/ 08 мая 2020

Это ответ Jest / Node только для тех, кто не поддерживает sh подключение каких-либо сторонних имитационных библиотек, таких как aws -sdk-mock .

Проблема (без отображения самой ошибки в вашем ответе), скорее всего, связана с .promise() в вашем коде реализации.

Вы добавили это в реализацию, чтобы сказать SDK вернуть вам обещание для любой операции, которая была вызвана.

await s3.putObject(params, callback).promise();

Это возвращенное обещание будет либо отклонено с ошибкой, либо разрешено с данными.

Это означает, что в подходе, основанном на обещаниях, вы можете полностью опустить обратный вызов .

await s3.putObject(params).promise();

(взято из this AWS сообщение в блоге )

Исправление обработчика ...

Вы можете:

Поместить этот лог обратного вызова c в последующие блоки цепочки обещаний:

.then((data) => {
  // ... do stuff
})
.catch((err) => {
  // ... handle error
}

или еще лучше (как кажется, вы уже принимаете) более современные Подход ES6:

Ожидание обещания putObject внутри в try-c Блок atch:

try {
  const data = await s3.putObject(params).promise()
  // ... do things with data on successful response
} catch (err) {
  // ... handle error
}

Собираем их вместе

Ваш обработчик должен выглядеть примерно так:

const { S3 } = require("aws-sdk");
const s3 = new S3();

exports.handler = async (event) => {
  const params = {
    Bucket: "some value",
    Key: "some key value",
    ContentType: "application/json",
    Body: "some body value",
  };

  try {
    const data = await s3.putObject(params).promise();

    // ... do stuff with data

    return {
      statusCode: 200,
      body: JSON.stringify(data),
      // ... etc.
    }
  } catch (err) {
    // ... handle error

    return {
      statusCode: 400, // or any 4XX, 5XX 
      body: '...',     // whatever you wish to return on error
      // ... etc.
    }
  }
}

Исправление тестов ...

Принимая во внимание, что вы можете опустить обратный вызов, тестовый код должен отразить лишнее .promise() в цепочке вызовов putObject в обработчике.

В тестовом файле макет SDK должен быть настроенным для: a) возврата конструктора верхнего уровня S3 b) чтобы сам конструктор S3 возвращал объект, содержащий функцию putObject c), чтобы этот putObject сам возвращал объект, содержащий promise function

Чтобы его можно было вызвать как настоящий SDK:

const { S3 } = require("aws-sdk"); // require("aws-sdk") returns { S3 }

const s3 = new S3()                //                    returns { putObject }

await s3.putObject(params)         //                    returns { promise }
        .promise();                //                    returns ...your_mock_response
// You need to return the { promise } here even if you don't care about
// mock calls beyond the putObject, because the handler itself calls .promise()
// and will throw "TypeError: Cannot read property 'promise' of undefined"

const putObjectMock = jest.fn(() => ({
  promise: jest.fn()
}));

jest.mock('aws-sdk', () => ({
  S3: jest.fn(() => ({
    putObject: putObjectMock,
  })),
}));

// S3 must have jest.fn(...) over an ordinary function otherwise
// the new S3() in the handler will fail.
// Jest does its magic with the function you provide to make it callable as a constructor

const myHandler = require("../../src/lambda/index");

// Don't forget to add the "async" before the "it" callback as your handler is async
it("has to mock s3#putObject", async () => {
  const params = {
    Bucket: "test1",
    Key: "test2",
    ContentType: "application/json",
    Body: "test3",
  };

  await handler(); // Call the handler to then assert against the mock params

  expect(putObjectMock).toHaveBeenCalledWith(params);
});

Заключительное примечание - добавьте свой обработчик import после макета настройки в предотвратить ошибку "Cannot access 'putObject' before initialization" (вызванную требованием SDK обработчика).

Надеюсь, это поможет!

...