Nikolas Knickrehm

1 min read

Using Mocks in Unit Tests for Lambda Handlers with Jest

Software Development

Today I had a little confrontation with Jest while writing unit tests for a Lambda function in TypeScript. The function is using the AWS SDK to send messages to an SQS queue. While this mocking problem seems very specific at first, mocking other dependencies would yield similar results.

Using Mocks in Unit Tests for Lambda Handlers with Jest

Here is a trivial example of the code:

import { SQS } from 'aws-sdk'

const sqsClient = new SQS({ apiVersion: '2012-11-05' })

export async function handle(event: SQSEvent): Promise<void> {
  await sqsClient.sendMessage({...}).promise()
}

While writing the unit tests I noticed that it was very hard to mock the AWS SDK correctly, when the handler method is imported normally in the unit test file.

import { SQS } from 'aws-sdk'
import { mock } from 'jest-mock-extended'
import { handle } from 'src/lambda-handlers'

jest.mock('aws-sdk')

it('should send a SQS message', async () => {
  const sqsRecordMock = mock<SQSRecord>()
  sqsRecordMock.body = '{...}'

  const sqsEventMock = {
    Records: [sqsRecordMock],
  }
  
  const sqsSendMessageSpy = jest.spyOn(SQS.prototype, 'sendMessage')
  sqsSendMessageSpy.mockReturnValueOnce({
    promise: async () => Promise.resolve(),
  })
  
  await handle(sqsEventMock)

  expect(sqsSendMessageSpy).toHaveBeenCalled() // ❌
})

Unfortunately, this natural approach does not work, because the mock is not set up correctly and in the end, the lambda handler will get undefined instead of the mocked AWS SDK through the import. After some trial and error, the easiest way to resolve this issue was to dynamically import the Lambda handler in the test after mocking the function of the AWS SDK.

import { SQS } from 'aws-sdk'
import { mock } from 'jest-mock-extended'

it('should send a SQS message', async () => {
  const sqsRecordMock = mock<SQSRecord>()
  sqsRecordMock.body = '{...}'

  const sqsEventMock = {
    Records: [sqsRecordMock],
  }
  
  const sqsSendMessageSpy = jest.spyOn(SQS.prototype, 'sendMessage')
  sqsSendMessageSpy.mockReturnValueOnce({
    promise: async () => Promise.resolve(),
  })
  
  const { handle } = await import('src/lambdaHandler')
  await handle(sqsEventMock)
  
  expect(sqsSendMessageSpy).toHaveBeenCalled() // ✅
})

Next up

Want to stay up to date?