Testing [Nodejs] Lambda functions
Opeyemi Obembe
Posted on March 26, 2018
Testing Lambda functions can be tricky. A typical cycle is to assume everything is fine, push up, get stuck, debug and make corrections locally, then push up again. A better approach would actually be to test locally before pushing up.
Let’s consider a typical Nodejs Lambda function
exports.handler = (event, context, callback) => {
// Do stuff here
return callback();
}
To be able to test this locally, we need to be able to pass in the required event
, context
and callback
parameters. Interestingly, by looking into the event
and context
objects and of course knowing callback
is just a function passed in to return information, we can create similar objects and use them for our local test.
Let’s start with event
. This is used to pass event data to the function. The data will depend on the event source connected to your Lambda function. You can find a list of sample events published by event sources here. Here is the sample event data for SNS for example:
{
"Records": [
{
"EventVersion": "1.0",
"EventSubscriptionArn": eventsubscriptionarn,
"EventSource": "aws:sns",
"Sns": {
"SignatureVersion": "1",
"Timestamp": "1970-01-01T00:00:00.000Z",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
"Message": "Hello from SNS!",
"MessageAttributes": {
"Test": {
"Type": "String",
"Value": "TestString"
},
"TestBinary": {
"Type": "Binary",
"Value": "TestBinary"
}
},
"Type": "Notification",
"UnsubscribeUrl": "EXAMPLE",
"TopicArn": topicarn,
"Subject": "TestInvoke"
}
}
]
}
For me though, (I use this on TFP to send data across the workers), two things:
- The only value I’m interested in from the event data is
event.Records[0].Sns.Message
-
Message
is a stringified JSON object.
Based on these, I can create my event
object as this:
var event = {
Records: [{
Sns: {
Message: JSON.stringify(testdata)
}
}]
}
Next is the context
parameter. We can see all the keys of the context object in this document. Let’s create a similar object.
var context = {
awsRequestId: Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5), // random string per request
callbackWaitsForEmptyEventLoop: true,
getRemainingTimeInMillis: function(){ return 0 },
functionName: '',
functionVersion: '',
memoryLimitInMB: '',
logGroupName: '',
logStreamName: '',
clientContext: null,
identity: null
}
callback
is the easy one.
var callback = function(err, result) {
if (err)
console.log(err);
if (result)
console.log(result);
// Terminate execution once done
process.exit(0);
}
Now that we have the 3 required parameters. We can put everything together.
require('dotenv').config(); // Environmental variables
var lambda = require('./path/to/lambda/function/index.js')
;
// Event
var eventdata = {
url: 'https://api.twitter.com/1.1/statuses/home_timeline.json'
, qs: {
tweet_mode: 'extended'
}
, user: '5ab7d745174f534889991a30'
, oauth: {
consumer_key: process.env['TWTR_CK']
, consumer_secret: process.env['TWTR_CS']
, token: process.env['TWTR_TOKEN']
, token_secret: process.env['TWTR_SECRET']
}
}
var event = {
Records: [{
Sns: {
Message: JSON.stringify(eventdata)
}
}]
}
// Context
var context = {
awsRequestId: Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5),
callbackWaitsForEmptyEventLoop: true,
getRemainingTimeInMillis: function(){},
functionName: '',
functionVersion: '',
memoryLimitInMB: '',
logGroupName: '',
logStreamName: '',
clientContext: null,
identity: null
}
// Callback
var callback = function(err, result) {
if (err)
console.log(err);
if (result)
console.log(result);
// Terminate execution once done
process.exit(0);
}
// Run
lambda.handler(event, context, callback);
And there we have it. If there is any issue with the function, we can easily know and debug. One tip during test is to do lots of console logging within your function to note execution points and see the data that is passed around.
Posted on March 26, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.