使用笑话在AWS Lambda函数中模拟对S3的调用

问题描述 投票:0回答:1

[我正在尝试使用Jest在本地Node.JS AWS Lambda函数中模拟对putObject的调用,但由于某些原因,我的期望值一直保持为0。

这是我的主要功能(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);
 }

我曾尝试将异步添加到我的测试函数中,因为我认为这是一个异步问题,但似乎仍然收到相同的错误。这是我的测试代码(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);
 });

任何帮助都会很棒。

node.js amazon-s3 aws-lambda mocking jest
1个回答
0
投票

这是一个Jest / Node专用答案,适用于那些不想引入任何第三方模拟库(例如aws-sdk-mock

问题(您的答案中没有看到错误本身)很可能与实现代码中的.promise()相关。

您已在实现中添加了此内容,以告知SDK对所调用的任何操作均向您返回承诺。

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

返回的诺言将要么拒绝,要么出错,或者使用数据解决。

这意味着在基于承诺的方法中,您可以完全省略回调

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

(从this AWS blog post中获取)

修复处理程序...

您可以:

将回调逻辑放在后续的promise链块中:
.then((data) => {
  // ... do stuff
})
.catch((err) => {
  // ... handle error
}

或更妙的是(看起来您已经在拥抱)更现代的ES6方法]

在try-catch块中等待putObject承诺:
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函数的对象

以便可以像真正的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);
});

最后说明-添加处理程序导入之后

模拟设置,以防止发生"Cannot access 'putObject' before initialization"错误(由处理程序对SDK的要求引起)。

希望这会有所帮助!

© www.soinside.com 2019 - 2024. All rights reserved.