DEV Community

Stan Hannebelle for Kumo

Posted on

The Best Integration Testing Library for Serverless Projects Now Supports Typescript!

Local development is an anti-pattern in the serverless world. If you're not convinced yet, have a look at Gareth McCumskey's talk given at Serverless Days Paris. 🎤

Having a local replica of the AWS resources deployed in production is not recommended as local emulations will not behave exactly like the production stack.

In this context, building a relevant integration testing strategy becomes painful as integration tests in a local environment do not guarantee a correct behavior in production.

Therefore, when it comes to integration tests, as a serverless developer you should:

  • Avoid mocking serverless services and testing your application in a local environment ❌
  • Use real underlying serverless services as much as possible to test your application ✅

How can you achieve that? 🤔
Using sls-test-tools 🏅

Image description

sls-test-tools provides custom Jest assertions to easily implement effective and high-quality integration tests for serverless architectures on AWS.

1️⃣  sls-test-tools was not typed 😭 but it now supports TypeScript 😍 !

sls-test-tools is a JS project and doesn’t provide types for TS developers. 🤯

However, at Kumo, our standard in terms of programming language for serverless projects is TypeScript.

Image description

Not having a tool to properly test our serverless TS projects has been an important issue for us in terms of developer experience and stability of our delivered applications.

So I migrated sls-test-tools from JS to TS; enabling the whole serverless/TS community to leverage sls-test-tools.

💡 I typed it using ts-migrate
ts-migrate is a tool I strongly recommend. It's been developed by Airbnb to help to migrate easily JavaScript projects to TypeScript.

The typed version of sls-test-tools has now been released (version 1.0.7 ) so:

  • ⭐⭐⭐  sls-test-tools can be used in TS projects ⭐⭐⭐
  • sls-test-tools API didn’t change for JS projects 🤝

2️⃣  New to sls-test-tools? Here is a quick tutorial

Use case

As an example, let’s consider a lambda function that publishes an event containing client contact details on EventBridge.

Image description

export const handler = async (): Promise<string> => {
  const eventPayload = {
    id: 'clientId',
    email: 'client@mail.com',
  };

  await customerCreatedEvent.publish(eventPayload);

  return eventPayload.id;
};
Enter fullscreen mode Exit fullscreen mode

Step 1

This lambda function firstly sets an Event payload.

  const eventPayload = {
    id: 'clientId',
    email: 'client@mail.com',
  };
Enter fullscreen mode Exit fullscreen mode

This block could typically be replaced by data retrieved from any appropriate database service and some business logic.

Step 2

The lambda function publishes the event to an EventBridge bus.

await customerCreatedEvent.publish(eventPayload);
Enter fullscreen mode Exit fullscreen mode

Let’s test this!

To be sure that this lambda correctly publishes an event to the EventBridge bus, let’s write an integration test thanks to sls-test-tools.

import { AWSClient, EventBridge } from 'sls-test-tools';

const lambda = new AWSClient.Lambda();

describe('Event Bridge Integration Testing ', () => {
  it('correctly publishes an event to the event bus when the lambda is invoked', async () => {
    // Step 1: Arrange
    const eventBridge = await EventBridge.build(busName);

    // Step 2: Act
    await lambda.invoke({ FunctionName: lambdaName }).promise();

    // Step 3: Assert

    const eventBridgeEvents = await eventBridge.getEvents();

    expect(eventBridgeEvents).toHaveEvent();
    expect(eventBridgeEvents).toHaveEventWithSource('client');

    // Step 4: Clean
    await eventBridge.destroy();
  });
});
Enter fullscreen mode Exit fullscreen mode

Step 1: Arrange - Update the real stack to run tests

const eventBridge = await EventBridge.build(busName);
Enter fullscreen mode Exit fullscreen mode

Firstly, the build function provided by sls-test-tools is called. It checks that your stack includes an EventBridge bus with a given name. It also deploys an SQS queue used as a destination for all events sent in the bus. This queue is used to watch specific events for your Jest test asserts.

Step 2: Act - Execute the lambda

await lambda.invoke({ FunctionName: lambdaName }).promise();
Enter fullscreen mode Exit fullscreen mode

Secondly, the lambda is executed.

Step 3: Assert - Expect real change on the stack

const eventBridgeEvents = await eventBridge.getEvents();

expect(eventBridgeEvents).toHaveEvent();
expect(eventBridgeEvents).toHaveEventWithSource('client');
Enter fullscreen mode Exit fullscreen mode

Then, the getEvents function is called. It returns published messages present in the SQS queue which contain information about published events in EventBridge Bus.

These messages are used to run two assertions to check that during the execution of the lambda:

  • An event has been published to the bus
  • An event has been published to the bus with a source value of client

Step 4: Clean - Discard any modification to the stack made during the test

await eventBridge.destroy();
Enter fullscreen mode Exit fullscreen mode

Finally, the SQS queue is destroyed in order to clean the tested stack.

3️⃣  Next steps

Our team, with the support of aleios and the community, is working on improving the DX of sls-test-tools (using environment variables instead of cli arguments...) and on adding more custom Jest assertions. We are also writing a more exhaustive article on Serverless integration testing, please feel free to subscribe so you can be notified when it will come out!

The community of sls-test-tools contributors is active and growing. Don’t hesitate to create an issue or open a PR if you want to get involved in the development of the project!

Discussion (1)

Collapse
redeemefy profile image
redeemefy

Why the repo README states that the project is in alpha but the release is already in 1.x?