DEV Community

Rudi Starcevic
Rudi Starcevic

Posted on

Serverless Framework API End-to-End Testing using Jest and Typescript

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 were 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

Oldest comments (0)