How to Set Up Jest in Serverless: A Step-by-Step Guide

Mrugank Ray
7 min readMar 23, 2024
Photo by Joan Gamell on Unsplash

Overview:

The goal of this article is to provide a comprehensive, step-by-step guide on how to set up Jest, a popular JavaScript testing framework, in an Serverless environment.

Serverless(AWS):

In our project we are using serverless-bundle library to package our lambda function with all its dependencies into a single, deployable file. This approach allows us to easily manage and deploy our Lambda function without the hassle of configuring webpack.

This package configures webpack to use babel-loader to transpile the code to ES5 compatible code, making it suitable for running in the AWS Lambda environment.

Thus, you can use ESM syntax in your codebase without worrying about compatibility issues.

Source Code:

You can find the source code for this project on GitHub at: https://github.com/mrugankray/config-jest-for-serverless

Setup:

To setup a project, follow these steps:

  1. Run `npm install -g serverless` to install the Serverless Framework globally on your machine.
  2. Run `serverless`. This will the start the setup process. For this project, you can choose Starter template, since it provides a basic structure for an AWS Lambda function.
  3. Also run npm install jest@29.7.0 to install jest.

Create a util function

Let’s create util.js file & create an add function inside it. Below is an example of how the util.js file would look like:

// util.js

export const add = (a,b) => {return a + b}

Lambda Handler

// index.js

import { add } from "./util"

export const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify(
{
message: add(1,2)
},
null,
2
),
};
};

Test for Handler

Let’s create a index.test.js file in the same directory as the index.js file.

Below is a sample test for the Lambda handler function:

// index.test.js

import { handler } from "./index"

beforeEach(() => {
jest.resetAllMocks()
})

test("test for adding two numbers", async () => {
const event = {
headers: {
"Content-Type": "application/json"
// ... other headers
},
body: "dummy body"
}

const response = await handler(event)

expect(response.statusCode).toEqual(200)
})

Jest

Jest is a popular JavaScript testing framework that provides a comprehensive set of features for writing and executing tests, including assertions, mocking, and code coverage

However, when running jest in a traditional Node.js environment, you are not using webpack to transpile code. This means when you run

jest --coverage

You will get the below error

ESM syntax error

This happens because nodejs expects you to use commonjs syntax rather than ESM syntax. But as you can see, in the index.js we are using ESM syntax to import the add function from the util.js file.

Use Plugin to run tests

As mentioned above, we use serverless-bundle to transpile code & package our Lambda function. We can use the same plugin to run our tests.

To run the test, add a test script in your package.json file. The test script in package.json should look like this:

// package.json

... other config
"scripts": {
"test": "serverless-bundle test",
"local": "sls offline --stage=develop"
},
Running tests using serverless-bundle plugin

However, serverless-bundle creates an artifact, which is unusually bigger than the original source code of your Lambda function. This will not impact if your service is small but as it grows in size, the increased artifact size may lead to deployment failures as AWS has a hard limit on the artifact size for Lambda functions.

You can read more about limits here

To address this issue, you can use the serverless-webpack plugin. Unlike serverless-bundle where you can’t customize webpack configurations, serverless-webpack allows you to add custom Webpack configurations. This allows you to optimize the size of your Lambda function’s artifact and customize webpack configurations for better performance and flexibility.

Use Babel Jest

Another way to run your tests is by using Babel

To use Babel with jest, you need to add some packages to your dev-dependencies. Below is the sample code you can add to your package.json file.

// package.json

... other config
"devDependencies": {
"@babel/core": "7.24.1",
"@babel/preset-env": "7.24.1",
"@babel/preset-typescript": "7.24.1",
"babel-jest": "29.7.0",
"dotenv": "16.4.5",
"jest": "29.7.0",
"serverless-bundle": "^6.1.0",
"serverless-offline": "^13.3.3",
"ts-jest": "29.1.2"
},

Also, modify your scripts sections to use jest like this

// package.json

... other config
"scripts": {
"test": "jest --coverage",
"local": "sls offline --stage=develop"
},

Setting up Jest Configuration

Before using Jest we need to setup some confguration in a jest.config.js file.

You can use the following sample code as a starting point for your jest.config.js file

// jest.config.js

module.exports = {
"testEnvironment": "node",
"transform": {
"^.+\\.(js)$": "babel-jest", // transpiles your code first using babel before running tests
"^.+\\.(ts|tsx)$": "ts-jest"
},
setupFiles: ["./setupTests.js"],
testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ], // this is default, you can change based on your needs
testPathIgnorePatterns: ["/node_modules/"],
testTimeout: 5000, // this is the default value, you can increase it based on your needs.
collectCoverage: true,
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}, // optional
collectCoverageFrom: [
"./index.js"
// you can add any folder where your code is
], // optional
coveragePathIgnorePatterns: [
"/node_modules/",
// add any file/folder for which you don't want coverage to be calculated
] // optional
}

Let’s understand the code:

  • Line 4: We instruct Jest to use babel-jest to transpile the code first before running the tests.
  • Line 7: We use setupFiles to specify a setup file that will be executed before running tests. We will create `./setupTests.js` in the next step
  • Line 8: We sepcify testMatch to define the pattern for Jest to find tests files.
  • Line 9: We specify testPathIgnorePatterns to exclude certain folders or files from being considered for testing.

Setting up setupTests.js

In this file you can import any necessary setup or configuration for your tests, such as mocking libraries or setting up test-specific environment variables.

Below is a sample env setup

// setupTests.js

import 'dotenv/config'

Configuring Babel

To configure Babel for Jest, you need to create a file called babel.config.js in the root of your project. Below is a sample babel.config.js file:

// babel.config.js

module.exports = api => {
api.cache(true)
const presets = [["@babel/preset-env", {
"targets": {"node": 20},
"modules": "commonjs"
}]]
const plugins = []

return { presets, plugins }
}

Running Jest

Execute the below command in the terminal to run Jest and start running your tests:

jest --coverage

As you can see, after using babel-jestand configuring Jest with the necessary settings, you can easily run your tests and generate coverage reports for your code.

Jest result(Success)

Use packages that uses ESM syntax internally

Now, let’s use a package node-fetch@3.3.2.

Let’s add it in package.json as devDependencies

// package.json

... other config
"node-fetch": "^3.3.2"

Let’s import it in index.js

// index.js

import fetch from 'node-fetch';

... other code

Now, if you run the test, you will get the following error:

Node fetch error

Transpile node packages

To transpile node packages, you can configure Jest to use @babel/plugin-transform-modules-commonjs plugin.

Let’s add it in package.json as devDependencies

// package.json

"devDependencies": {
// ... other packages
"@babel/plugin-transform-modules-commonjs": "7.24.1",
},

Updating Jest Configuration

Add the following to instruct Jest to transpile node packages.

// jest.config.js

module.exports = {
// ... other config
transformIgnorePatterns: [], // to transpile code in node modules too,
}

Regular expression patterns in the array are checked against all source file paths before making any transformations. If the file path matches any of the patterns, it won’t undergo transformation. As we don’t specify any regex pattern in the example, all source files in the node_modules folder will be transpiled by Jest.

Updating Babel Configuration

We need to insert the following code snippet in the babel.config.js file to include the required plugin for transpiling node packages:

// babel.config.js

// ... other config
const plugins = ["@babel/plugin-transform-modules-commonjs"]

Running Jest

Now, when you run the test. Now, when you run the test, Jest will transpile the your codebase along with the node packages and then run the tests, allowing for proper execution and accurate coverage reports.

Below is the result after running the tests:

Tests result

Conclusion

If you have a small service and you don’t expect it to grow significantly, using a simpler and faster setup for your runtime environment, such as serverless-bundle makes sense.

However, if you’re unsure about the potential growth of your project, it is recommended to use serverless-webpack and configure Jest with the necessary settings mentioned above to transpile code, enabling a more flexible and scalable solution for your runtime environment.

--

--