Unit Test and Integration Test for AWS Lambda/NodeJS in TypeScript
AWS Lambda/NodeJS (TypeScript) Series
- How to Use TypeScript for AWS Lambda in 3 Steps
- 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:
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:
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!