Unit Test and Integration Test for AWS Lambda/NodeJS in TypeScript

Zi Zhao
Level Up Coding
Published in
4 min readOct 5, 2020

--

AWS Lambda/NodeJS (TypeScript) Series

  1. How to Use TypeScript for AWS Lambda in 3 Steps
  2. Unit Test and Integration Test for AWS Lambda/NodeJS in TypeScript

Preparation

We are going to use jest to manage all the tests. In order to use jest with TypeScript, we will have to install some babel dependencies. Please check the installation commands below:

// install jest and types
$ npm i -D jest @types/jest
// install babel support to use jest with typescript
$ npm i -D babel-jest @babel/core @babel/preset-env @babel/preset-typescript

We install them in dev-dependencies because they are only used during testing, not in the final built package. After installation, we need to create the babel.config.js in the to make babel work. File content:

module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};

The file structure now looks like:

.
├── README.md
├── hello-world
│ ├── babel.config.js
│ ├── package-lock.json
│ ├── package.json
│ ├── src-ts
│ └── tsconfig.json
├── samconfig.toml
└── template.yaml

Unit Test

To use jest, it is best to have a config file, we can generate the file with jest cli:

// pwd: $ROOT/hello-world
$
./node_modules/.bin/jest init // generate jest.config.js
$ mv jest.config.js jest.config.test.js // rename

In the generated jest.config.test.js, we need the following options:

module.exports = {
clearMocks: false,
collectCoverage: true,
coverageDirectory: "coverage",
coverageProvider: "v8",

testEnvironment: "node",
testMatch: [
"**/unit/**/*.test.ts"
],
};

Then let’s create our first unit test file hello-world/tests/unit/test-handler.test.ts. Next we could fill in the file with a unit test for the app handler created in the last article.

import { APIGatewayProxyEvent } from "aws-lambda";
import { lambdaHandler } from "../../src-ts/app";

describe('Unit test for app handler', function () {
it('verifies successful response', async () => {
const event: APIGatewayProxyEvent = {
queryStringParameters: {
a: "1"
}
} as any
const result = await lambdaHandler(event)

expect(result.statusCode).toEqual(200);
expect(result.body).toEqual(`Queries: ${JSON.stringify(event.queryStringParameters)}`);
});
});

Add a new command in package.json, so that we can run the unit test easily:

"scripts": {
"test": "jest --config=jest.config.test.js",
"compile": "tsc"
}

Now, we should be able to run the unit test and see the coverage report:

Unit test run result

Looks great! The file structure looks like this now:

.
├── README.md
├── hello-world
│ ├── babel.config.js
│ ├── jest.config.test.js
│ ├── package-lock.json
│ ├── package.json
│ ├── src-ts
│ ├── tests
│ │ └── unit
│ │ └── test-handler.test.ts

├── samconfig.toml
└── template.yaml

If you find this article useful, please follow this account for future updates. Thanks for the support!

Integration Test

To distinguish integration test from unit test, we need a different jest config file, namely jest.confg.integ.test.js with content:

// no need for coverage here
module.exports = {
testEnvironment: "node",
testMatch: [
"**/integ/**/*.integ.test.ts"
],
};

To run integration test, we will need the following two more things:

  • A local server running our lambda
  • A HTTP SDK package to send request to the local server

Run the lambda in a local server

Fortunately, AWS SAM CLI provides the ability to run the stack locally, with this command:

// pwd: $ROOT/hello-world
$ sam local start-api -t ../template.yaml

This command requires installation of Docker, you could refer to this doc for more information. After execution, it will bring up a server listening at 127.0.0.1:3000.

It is better to add the above command to NPM scripts too:

"scripts": {
"test": "jest --config=jest.config.test.js",
"start-local": "sam local start-api -t ../template.yaml",
"compile": "tsc"
},

Add HTTP SDK to the project

We would choose Axios to send request to the server started above:

$ npm i -D axios @types/axios

Create an integration test file hello-world/tests/integ/handler.integ.test.ts, with content:

import axios from "axios";

describe("Integration Test", () => {
it("hello world integration test", async () => {
const query = { a: "hi" };
const response = await axios.get("http://localhost:3000/hello", {
params: query
});

expect(response.status).toEqual(200);
expect(response.data).toEqual(`Queries: ${JSON.stringify(query)}`);
});
});

In this integration test, we use axios to send a GET message to the server started above. What’s more, we use async and await to keep the test code simple and readable.

Again, add an execution command for the integration test in package.json:

"scripts": {
"test": "jest --config=jest.config.test.js",
"integ-test": "jest --config=jest.config.integ.test.js",
"start-local": "sam local start-api -t ../template.yaml",
"compile": "tsc"
}

Run the integration test

We need two consoles for integration test, one for the server, another one for running the integration test. Take a look at the running result:

Integration Test Execution

The final file structure:

.
├── README.md
├── hello-world
│ ├── babel.config.js
│ ├── jest.config.integ.test.js
│ ├── jest.config.test.js
│ ├── package-lock.json
│ ├── package.json
│ ├── src-ts
│ │ └── app.ts
│ ├── tests
│ │ ├── integ
│ │ │ └── handler.integ.test.ts

│ │ └── unit
│ │ └── test-handler.test.ts
│ └── tsconfig.json
├── samconfig.toml
└── template.yaml

Conclusion

Awesome! We now can not only write lambda handlers in TypeScript, but also compose unit/integration tests with TypeScript. The full code can be found at: https://github.com/zijing07/aws-lambda-nodejs-ts.

In next article, I will share how to create unit/integration tests with DDB in AWS Lambda.

If you find this article useful, please follow this account for future updates. Thanks for the support!

--

--