如何使用NodeJS在本地测试AWS Lambda处理程序?

49

我正在遵循这些指令来创建在Lambda中执行的基本网络爬虫。我有编写selenium代码的经验,但不熟悉Node JS。我已经将项目在Lambda上运行,但是当我尝试本地编辑项目以执行想要的selenium代码时,它无法工作。当我运行node index.js时,exports.handler中的任何内容都不会被执行。我该如何在本地执行此项目?谢谢!


你需要调用你的 function,而不仅仅是导出声明。在 index.js 中将你导出的 handler.js 分配给一个变量,并调用你的函数。例如:(假设 exports.handler 是一个 function。) - bradcush
8个回答

77

这是我所做的:

index.js

exports.handler = async (event) => {
    console.log('hello world');

    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!')
    };

    return response;
};

package.json

"scripts": {
  "locally": "node -e \"console.log(require('./index').handler(require('./event.json')));\""
}

event.json

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "eu-central-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "AIDAJDPLRKLG7UEXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "C3D13FE58DE4C810",
        "x-amz-id-2": "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "my-bucket",
          "ownerIdentity": {
            "principalId": "A3NL1KOZZKExample"
          },
          "arn": "arn:aws:s3:::my-bucket"
        },
        "object": {
          "key": "HelloWorld.jpg",
          "size": 1024,
          "eTag": "d41d8cd98f00b204e9800998ecf8427e",
          "versionId": "096fKKXTRTtl3on89fVO.nfljtsv6qko"
        }
      }
    }
  ]
}

Shell

npm run locally

输出

> node -e "console.log(require('./index').handler({}));"

hello world
Promise { { statusCode: 200, body: '"Hello from Lambda!"' } }

为什么要多一个 npm 步骤呢?直接运行 node 命令不是更简单明了吗? - Jon Buys
3
那个额外的npm步骤并不是必要的。它只是为了方便我,因为我可以添加特定于该代码库的参数来调用。而且说实话,这对我来说更容易记住 :) - jmk
2
在这种情况下,我们如何传递事件? - Vishnu
我喜欢这种便利。但是,如果您想针对Lambda函数运行10个不同的测试场景,该怎么办?您如何传递事件的不同变化 - 快乐路径、悲伤路径、空事件、无效数据等? - mojave
1
你可以创建一个名为sad-event.json的新json文件,并要求该文件而不是"./event.json"。你可以将它们组织在一个名为test-events的文件夹中,这样你的路径就可以是"test-events/happy.json"。 - pegasuspect
你的 package.json 不是有效的 JSON 格式。这个写法从来没有真正起作用过。 - larsks

49

为了通过NodeJs运行,您需要从另一个文件中调用处理函数,假设是testHandler.js

可以按照以下方式完成:

//import your handler file or main file of Lambda
let handler = require('./handler');

//Call your exports function with required params
//In AWS lambda these are event, content, and callback
//event and content are JSON object and callback is a function
//In my example i'm using empty JSON
handler.handlerEvent( {}, //event
    {}, //content
    function(data,ss) {  //callback function with two arguments 
        console.log(data);
    });

现在你可以使用node testHandler.js来测试你的处理程序函数。

编辑:根据要求提供样例事件和内容数据

事件:

{
    "resource": "/API/PATH",
    "path": "/API/PATH",
    "httpMethod": "POST",
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
        "cache-control": "no-cache",
        "CloudFront-Forwarded-Proto": "https",
        "CloudFront-Is-Desktop-Viewer": "true",
        "CloudFront-Is-Mobile-Viewer": "false",
        "CloudFront-Is-SmartTV-Viewer": "false",
        "CloudFront-Is-Tablet-Viewer": "false",
        "CloudFront-Viewer-Country": "IN",
        "content-type": "application/json",
        "Host": "url.us-east-1.amazonaws.com",
        "origin": "chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop",
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
        "Via": "2.0 XXXXXXXXXXXXXX.cloudfront.net (CloudFront)",
        "X-Amz-Cf-Id": "XXXXXXXXXX51YYoOl75RKjAWEhCyna-fuQqEBjSL96TMkFX4H0xaZQ==",
        "X-Amzn-Trace-Id": "Root=1-XXX03c23-25XXXXXX948c8fba065caab5",
        "x-api-key": "SECUREKEY",
        "X-Forwarded-For": "XX.XX.XXX.XXX, XX.XXX.XX.XXX",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "Accept": [ "*/*" ],
        "Accept-Encoding": [ "gzip, deflate, br" ],
        "Accept-Language": [ "en-GB,en-US;q=0.9,en;q=0.8" ],
        "cache-control": [ "no-cache" ],
        "CloudFront-Forwarded-Proto": [ "https" ],
        "CloudFront-Is-Desktop-Viewer": [ "true" ],
        "CloudFront-Is-Mobile-Viewer": [ "false" ],
        "CloudFront-Is-SmartTV-Viewer": [ "false" ],
        "CloudFront-Is-Tablet-Viewer": [ "false" ],
        "CloudFront-Viewer-Country": [ "IN" ],
        "content-type": [ "application/json" ],
        "Host": [ "apiurl.us-east-1.amazonaws.com" ],
        "origin": [ "chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop" ],
        "User-Agent": [ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" ],
        "Via": [ "2.0 XXXXXXXXXXXXXX.cloudfront.net (CloudFront)" ],
        "X-Amz-Cf-Id": [ "XXXXXXXXXhCyna-fuQqEBjSL96TMkFX4H0xaZQ==" ],
        "X-Amzn-Trace-Id": [ "Root=1-XXXXXXX67339948c8fba065caab5" ],
        "x-api-key": [ "SECUREAPIKEYPROVIDEDBYAWS" ],
        "X-Forwarded-For": [ "xx.xx.xx.xxx, xx.xxx.xx.xxx" ],
        "X-Forwarded-Port": [ "443" ],
        "X-Forwarded-Proto": [ "https" ]
    },
    "queryStringParameters": null,
    "multiValueQueryStringParameters": null,
    "pathParameters": null,
    "stageVariables": null,
    "requestContext": {
        "resourceId": "xxxxx",
        "resourcePath": "/api/endpoint",
        "httpMethod": "POST",
        "extendedRequestId": "xxXXxxXXw=",
        "requestTime": "29/Nov/2018:19:21:07 +0000",
        "path": "/env/api/endpoint",
        "accountId": "XXXXXX",
        "protocol": "HTTP/1.1",
        "stage": "env",
        "domainPrefix": "xxxxx",
        "requestTimeEpoch": 1543519267874,
        "requestId": "xxxxxxx-XXXX-xxxx-86a8-xxxxxa",
        "identity": {
            "cognitoIdentityPoolId": null,
            "cognitoIdentityId": null,
            "apiKey": "SECUREAPIKEYPROVIDEDBYAWS",
            "cognitoAuthenticationType": null,
            "userArn": null,
            "apiKeyId": "xxXXXXxxxxxx",
            "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
            "accountId": null,
            "caller": null,
            "sourceIp": "xx.xxx.xxx.xxx",
            "accessKey": null,
            "cognitoAuthenticationProvider": null,
            "user": null
        },
        "domainName": "url.us-east-1.amazonaws.com",
        "apiId": "xxxxx"
    },
    "body": "{\n    \"city\": \"Test 1 City\",\n    \"state\": \"NY\",\n    \"zipCode\": \"11549\"\n}",
    "isBase64Encoded": false
}

内容:

{
    "callbackWaitsForEmptyEventLoop": true,
    "logGroupName": "/aws/lambda/lambda-name",
    "logStreamName": "2018/11/29/[$LATEST]xxxxxxxxxxxb",
    "functionName": "lambda-name",
    "memoryLimitInMB": "1024",
    "functionVersion": "$LATEST",
    "invokeid": "xxxxx-xxx-11e8-xxx-xxxxxxxf9",
    "awsRequestId": "xxxxxx-xxxxx-11e8-xxxx-xxxxxxxxx",
    "invokedFunctionArn": "arn:aws:lambda:us-east-1:xxxxxxxx:function:lambda-name"
}

你能展示一下在AWS Lambda上调用event通常是什么样子的例子吗? - Petrus Theron
2
@PetrusTheron 添加了示例事件和内容。 - NAVIN
根据事件的触发器,事件的外观会有所不同。在Web UI中编辑lambda时,测试事件选择下拉菜单中有“配置测试事件”作为其中之一的选项。在那里,他们为所有类型的触发器提供原型事件。 - Zag

2

也许最简单的方法是在测试后(使用 Node 14.17.3)开始

let handler = require('./index.js');

handler.handler ( 
  {}, // event
  {}, // content 
  (error, result) => { 
     if (error) console.error(JSON.stringify(error, null, 2));
     else console.log(JSON.stringify(result, null, 2));
  }
);

2
在你的index.js中,你只是定义并导出了一个处理程序函数,但没有人调用它。在Lambda环境中,一些AWS代码将使用消息调用此处理程序。在本地环境中,你必须自己调用处理程序。
你也可以查看这个文档,这是一种在本地环境中“模拟”Lambda的方法。

2
您可以查看lambda-local。它比上面接受的答案稍微高级一些。例如,它支持传递环境变量并使用JSON文件作为负载。

2

我将为您提供一种同步调用的通用解决方案。

在您的入口文件index.js中的函数签名:
exports.handler = function(event, context, callback) {

  1. 创建一个调用者文件,例如testIndex.js
  2. 给它以下内容:
    /**
    This is a caller for ./index.js
    */
    const thatIndex = require('./index');

    const EVENT = {
      "somekey": {
        "somesubkey": "somevalue"
      }
    }

    thatIndex.handler (
        // event
        EVENT,
        // context
        {},
        // callback function with two arguments
        function(err, payload) {
            console.log(err);
            console.log(payload);
        }
    );
  1. 回到你的节点控制台(建议使用Docker容器)
  2. 如果你的index.js需要环境变量,你可以使用它们:定义每个变量为export MYVAR1=myvalue1
  3. 运行你的调用程序:node testIndex.js
  4. 控制台应该输出有效载荷,前提是你的主文件(入口点)promise调用已经链接起来了:(例如)
    .then(() => callback(null, {
       statusCode: 301,
       headers: {'location': process.env.URL + '/' + somekey },
       body: '',
       })
    )
    .catch(err => callback(err))

提示:如果您发现调用的是以前的代码,请使用node clean cache清理缓存。

其他提示:在AWS环境中,您不需要压缩node_modules/aws-sdk文件夹,因为它将成为标准执行上下文的一部分。但是在测试计算机上,您需要这样做。因此,在开发模式下,只需运行npm install,而不是npm install --omit=dev

因此,在压缩包含函数的文件夹之前,只需递归删除node_modules文件夹,并使用npm install --ommit=dev重新构建依赖项。

压缩文件夹的示例: zip -r ..\resizeImage.zip .

这是您的package.json文件可能包含的内容:

  "dependencies": {
    "sharp": "^0.27.2"
  },
  "devDependencies": {
    "aws-sdk": "^2.1195.0"
  }

1

0

在我看来,测试lambda的最佳方法就是实际上进行测试!这意味着什么?只需使用一些测试库(例如jest),并简单地创建一个关于您的handler函数的测试。模拟您需要的任何内容,并提供一些您期望进入lambda的数据(如果需要,则为eventcontext)。就这样。您已经编写了一些测试,并且可以快速测试您的lambda。


如果您正在使用Terraform部署Lambda,我可以推荐一个支持TypeScript的打包工具,它是我自己开发的。https://www.npmjs.com/package/funpack - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接