how to unit test if handler function is called when using middy

4.7k Views Asked by At

im using an http request function as the handler function in middy and then use the ssm middleware to fetch some ssm parameters before initiating the http request. like this:

  const makeThirdPartyServiceRequest = middy(async ({ params }) => {
  logger.info(`SENDING Request to ${endpoint} API`)
  const url = `https://someurltoathirdpartyservice`
  const options = {
    method: 'POST',
    body: params
  }

  return helpers.makeRequest(url, options)
})
makeThirdPartyServiceRequest.use(ssm(......))

However in my jest unit test Im trying to mock makeThirdPartyServiceRequest and explicitly say it should resolve to a value:

jest.mock('../src/thirdPartyService', () => ({
  __esModule: true,
  default: {
    ...(jest.requireActual('../src/thirdPartyService') as { default: {} }).default,
    makeThirdPartyServiceRequest: jest.fn()
  }
}))
export {}
import thirdPartyService from '../src/thirdPartyService'

And then in the test i say:

describe('makeThirdPartyServiceRequest()', () => {
  it('should makeThirdPartyServiceRequest', async () => {
    // Given

    // })
    const mockedThirdPartyServiceRequest = mocked(thirdPartyService.makeThirdPartyServiceRequest).mockResolvedValue({})
    // When
    const result = await thirdPartyService.makeThirdPartyServiceRequest(something)
    // Then
    expect(mockedThirdPartyServiceRequest).toHaveBeenCalledTimes(1)
    expect(mockedThirdPartyServiceRequest.mock.calls[0][0].params.toString()).toBe(expectedParams)
  })
})

However for some reason the middy middleware is still being invoked, which i clearly dont want and i have tried to mock away... what am i doing wrong?

2

There are 2 best solutions below

2
On BEST ANSWER

You need to mock middy instead, to make it becomes a useless function. That function recipe a function as a parameter and return that parameter.

import thirdPartyService from '../src/thirdPartyService'

jest.mock('@middy/core', () => {
  return (handler) => {
    return {
      use: jest.fn().mockReturnValue(handler), // ...use(ssm()) will return handler function
    }
  }
})

describe('thirdPartyService()', () => {
  beforeEach(() => {
    jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
  })

  describe('makeThirdPartyServiceRequest', () => {
    it('should make a request with correct parameters', async () => {
      // Given
      const url = `https://someurltoathirdpartyservice`
      const params = 'any params'
      const apiResponse = 'any response'
      mocked(helpers.makeRequest).mockResolvedValue(apiResponse)

      // When
      const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)

      // Then
      expect(actual).toBe(apiResponse)
      expect(helpers.makeRequest).toHaveBeenCalledWith(
        url,
        {
          method: 'POST',
          body: params
        }
      )
    })
  })
})
0
On

hoangdv answer is also valid, but i will answer as well how i continued.

if you completely want to mock middy you mock like following:

    jest.mock('@middy/core', () => {
      return (handler) => {
        return {
          use: jest.fn().mockImplementation(() => {
            // ...use(ssm()) will return handler function
            return {
              before: jest.fn().mockReturnValue(handler)
            }
          })
        }
      }
    })

However if you dont want to completely mock middy, you can instead mock the async getInternal function from middy/util called in before like this:

    jest.doMock('@middy/util', () => ({
      ...(jest.requireActual('@middy/util') as {}),
      getInternal: jest.fn()
    }))
import { getInternal } from '@middy/util'

and then in the test

    describe('thirdPartyService()', () => {
      beforeEach(() => {
        jest.spyOn(helpers, 'makeRequest') // spy on helpers unit
      })
    
      describe('makeThirdPartyServiceRequest', () => {
        it('should make a request with correct parameters', async () => {
          // Given
          const url = `https://someurltoathirdpartyservice`
          const params = 'any params'
          const apiResponse = 'any response'
          mocked(getInternal).mockResolvedValue({
          twilioSecrets: { accountSid: 'someSID', serviceId: 
          'someServiceID', token: 'someToken' }
          })
          mocked(helpers.makeRequest).mockResolvedValue(apiResponse)
    
          // When
          const actual = await thirdPartyService.makeThirdPartyServiceRequest(params)
    
          // Then
          expect(actual).toBe(apiResponse)
          expect(helpers.makeRequest).toHaveBeenCalledWith(
            url,
            {
              method: 'POST',
              body: params
            }
          )
        })
      })
    })

this will mock the async part of middy.