Testing Lambda Functions Locally with Mocha & Chai for Integration Readiness
How to test complete flow after code change to find breaking changes
To ensure thorough testing of AWS Lambda functions locally, especially to catch integration issues before deploying to QA or production, you can use a combination of tools, libraries, and practices. Here's a step-by-step guide on how to achieve this, particularly using AWS Lambda with Node.js.
1. Setting up your Lambda function locally
Start by setting up your Lambda function in your local development environment. Use Node.js and structure your code in a way that allows for easy local testing.
Example Lambda function structure in Node.js:
// lambdaFunction.js
exports.handler = async (event, context) => {
try {
// Your lambda logic
return {
statusCode: 200,
body: JSON.stringify({ message: 'Success!' }),
};
} catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: error.message }),
};
}
};
2. Use SAM CLI (Serverless Application Model)
AWS provides SAM (Serverless Application Model) CLI, which allows you to simulate a Lambda environment locally. It’s the most reliable method because it emulates the AWS environment.
Steps to set up SAM CLI:
Install SAM CLI: Follow the instructions for your platform here.
Initialize your project: Use SAM CLI to create a basic project.
sam init
This will scaffold a new serverless project where you can add your Lambda function and its infrastructure.
Run locally: You can invoke the Lambda locally using:
sam local invoke "YourFunctionName" -e event.json
The
event.json
file contains your test event data that mimics what the Lambda function will receive.Testing API Gateway locally: If your Lambda is integrated with API Gateway, you can start an API Gateway local server:
sam local start-api
This allows you to test HTTP endpoints that trigger your Lambda functions.
3. Using Docker for Local Lambda Testing
If you don't want to use SAM, you can use Docker to mimic the Lambda environment by running a container.
Steps:
Install Docker on your system if you haven’t already: https://docs.docker.com/get-docker/
Pull the official AWS Lambda Docker image for Node.js (for example, Node.js 14):
docker pull public.ecr.aws/lambda/nodejs:14
Run your Lambda function within the container:
docker run -v "$PWD":/var/task:ro,delegated -e DOCKER_LAMBDA_USE_STDIN=1 public.ecr.aws/lambda/nodejs:14
This runs your Lambda function with the necessary Node.js runtime and allows you to test it locally.
4. Automating Testing with Mocha and Chai
You can write automated unit tests for your Lambda using Mocha and Chai, two popular Node.js testing libraries.
Setup Mocha and Chai:
Install Mocha and Chai:
npm install --save-dev mocha chai
Write unit tests:
Create a test
folder and write test cases for your Lambda:
// test/lambdaFunction.test.js
const { expect } = require('chai');
const lambdaFunction = require('../lambdaFunction');
describe('Lambda Function Tests', () => {
it('should return 200 on success', async () => {
const event = {}; // Simulate event
const response = await lambdaFunction.handler(event);
expect(response.statusCode).to.equal(200);
expect(JSON.parse(response.body).message).to.equal('Success!');
});
it('should handle errors properly', async () => {
const event = { causeError: true }; // Simulate an error scenario
const response = await lambdaFunction.handler(event);
expect(response.statusCode).to.equal(500);
});
});
Run the tests:
npx mocha test
5. Mocking AWS Services Locally
You can use AWS SDK mocks to simulate AWS services, ensuring that your Lambda function interacts with them correctly.
- Use aws-sdk-mock to mock AWS services:
npm install aws-sdk-mock --save-dev
In your test file, you can mock the service like this:
const AWSMock = require('aws-sdk-mock');
const { expect } = require('chai');
const lambdaFunction = require('../lambdaFunction');
AWSMock.mock('S3', 'getObject', (params, callback) => {
callback(null, { Body: 'file content' });
});
describe('Lambda S3 Interaction', () => {
it('should return file content', async () => {
const event = {}; // Simulated event with S3 parameters
const response = await lambdaFunction.handler(event);
expect(response.statusCode).to.equal(200);
});
});
6. End-to-End Testing with LocalStack
To test complete integration (including AWS services), use LocalStack. It provides a local environment for AWS services like Lambda, S3, DynamoDB, etc.
Setup LocalStack:
Install LocalStack:
pip install localstack
Run LocalStack:
localstack start
Configure AWS CLI to point to LocalStack:
export AWS_ACCESS_KEY_ID="test" export AWS_SECRET_ACCESS_KEY="test" export AWS_DEFAULT_REGION="us-east-1"
Deploy Lambda to LocalStack for testing:
Create an
aws-lambda-deploy.sh
script to package and deploy your Lambda using LocalStack.
#!/bin/bash
# Package the Lambda function
zip function.zip lambdaFunction.js
# Deploy to LocalStack
aws --endpoint-url=http://localhost:4566 lambda create-function --function-name TestFunction \
--zip-file fileb://function.zip --handler lambdaFunction.handler --runtime nodejs14.x \
--role arn:aws:iam::123456:role/execution_role
Now you can invoke your Lambda and simulate real AWS interactions.
7. Continuous Integration (CI)
Integrate your testing strategy into CI/CD pipelines (e.g., with Jenkins, GitHub Actions, or CircleCI) so that tests are automatically triggered with every commit.
Example GitHub Actions configuration (.github/workflows/test.yml
):
name: Node.js CI
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npx mocha
Conclusion
Testing AWS Lambda functions locally and ensuring that they are production-ready involves several steps:
Local Testing using SAM or Docker to mimic the Lambda environment.
Unit and Integration Testing using Mocha, Chai, and aws-sdk-mock to cover all edge cases.
LocalStack to simulate real AWS service interactions.
CI/CD Pipelines to automatically run tests for each change.
This strategy will help you detect integration issues early, ensuring smoother deployments to QA and production.