rudijs.github.com

Web Development, Web Operations, Devops Blog

AWS Typescript Serverless Framework API End to End testing using Jest

Overview

Test your AWS Typescript Serverless API’s using Jest

Sample code is here

If you find any typo’s or cut-n-paste errors or mistakes please let me know.

Please also comment if you have any improvement suggestions.

The follow steps and examples was created on April 15th 2020 using:

  • Node v12.16.1
  • and Serverless Framework versions:
Framework Core: 1.67.3
Plugin: 3.6.6
SDK: 2.3.0
Components: 2.29.1

Setup from scratch

The following steps are all command line on a Unix platorm, please adjust for your platorm where required.

Create a new directory and chnage to it (for example):

mkdir serverless-e2e-typescript-example

cd serverless-e2e-typescript-example

Create an AWS Lambda serverless API:

npx serverless create --template aws-nodejs-typescript --name api

Here’s an example of the output:

❯ npx serverless create --template aws-nodejs-typescript --name api
Serverless: Generating boilerplate...
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.67.3
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs-typescript"

Install the node dependencies

npm install

Edit the serverless.yml file so that we can set a default stage and region.

Under the provider section add:

stage: ${opt:stage, 'dev'}
region: ${opt:region, 'us-east-1'}

Lets make our API endpoint output only a message and not echo the Lambda input

Edit handler.ts, comment out line 9

// input: event

Deploy the API to the ‘dev’ stage.

Example deployment command and output:

> npx serverless --stage dev deploy
Serverless: Bundling with Webpack...
Time: 394ms
Built at: 04/15/2020 1:38:22 PM
         Asset      Size  Chunks                   Chunk Names
    handler.js  1.28 KiB       0  [emitted]        handler
handler.js.map  5.27 KiB       0  [emitted] [dev]  handler
Entrypoint handler = handler.js handler.js.map
[0] ./handler.ts 316 bytes {0} [built]
[1] external "source-map-support/register" 42 bytes {0} [built]
Serverless: Package lock found - Using locked versions
Serverless: Packing external modules: source-map-support@^0.5.10
Serverless: Packaging service...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service api.zip file to S3 (289.14 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............................
Serverless: Stack update finished...
Service Information
service: api
stage: dev
region: us-east-1
stack: api-dev
resources: 11
api keys:
  None
endpoints:
  GET - https://driyuairb6.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
  hello: api-dev-hello
layers:
  None
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

Lets call the new API endpoint, copy the hello endpoint from your deployment:

❯ curl https://driyuairb6.execute-api.us-east-1.amazonaws.com/dev/hello
{
  "message": "Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!"
}
``

Great! Our new typescript API endpoint is working, lets setup the end-to-end testing.

Install test dependencies:

npm i -D jest ts-jest @types/jest axios

Create a testing directory and Jest config file

mkdir e2e
touch e2e/jest.config.js

This is how your jest.config.js file should be:

module.exports = {
  testEnvironment: "node",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
  },
}

You can create any testing file structure you prefer but for this example we’ll be creating files of the test functions then importing them into a single test file describing all the tests in order and assigning them with the imported functions.

Create a test file for the hello API endpoint, I prefer to prefix with a number as it’s common to test API endpoints in sequence:

Create the file 100_hello.ts with the code content:

import axios from "axios"

const url = process.env.URL

export const helloTest = () => {
  test("should reply success", async () => {
    const res = await axios.get(`${url}/hello`)
    expect(res.status).toEqual(200)
    expect(res.data.message).toMatch(/Your function executed successfully!/)
  })
}

Create the test suite runner file index.test.js with the code content:

import { helloTest } from "./100_hello"

describe("hello", helloTest)

Lets run the end-to-end test manually first, then we’ll create an npm script to simplify it:

 URL=https://driyuairb6.execute-api.us-east-1.amazonaws.com/dev ./node_modules/.bin/jest -c e2e/jest.config.js  --runInBand --bail
ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.
 PASS  e2e/index.test.ts
  hello
    ✓ should reply success (474ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.059s, estimated 2s
Ran all test suites.

Yay! Our end-to-end test passes.

Let’s fix that ts-jest warning by adding "esModuleInterop": true in the tsconfig.json file

"esModuleInterop": true

Run the tests manually again:

URL=https://driyuairb6.execute-api.us-east-1.amazonaws.com/dev ./node_modules/.bin/jest -c e2e/jest.config.js --runInBand --bail
 PASS  e2e/index.test.ts
  hello
    ✓ should reply success (489ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.96s, estimated 2s
Ran all test suites.

Next lets create an npm script in package.json:

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"e2e": "jest -c e2e/jest.config.js --runInBand --bail e2e"
},

Now we can run our tests with:

URL=https://driyuairb6.execute-api.us-east-1.amazonaws.com/dev npm run e2e

> api@1.0.0 e2e /home/rudi/projects/serverless-e2e-typescript-example
> jest -c e2e/jest.config.js --runInBand e2e

 PASS  e2e/index.test.ts
  hello
    ✓ should reply success (474ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.001s
Ran all test suites matching /e2e/i.

Finally lets clean up with:

npx serverless --stage dev remove

npx serverless --stage dev remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
.............
Serverless: Stack removal finished...

Credits