4

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?

  • Every package within @middy has numerous examples of how to do this within the `__tests__` folders (https://github.com/middyjs/middy/tree/main/packages). v1.x of middy used `jest` before switching to `ava` in v2.x. – will Farrell Dec 27 '21 at 07:07

2 Answers2

4

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
        }
      )
    })
  })
})
hoangdv
  • 15,138
  • 4
  • 27
  • 48
  • Thanks alot for the reply! i followed your example but since im also using the before function, im getting an error that the before function is not a function: TypeError: (0 , core_1.default)(...).use(...).before is not a function – Muhammad Ali Nazar Dec 06 '21 at 08:36
  • Thanks again, this lead me to the right direction and will therefore mark this as a valid answer! – Muhammad Ali Nazar Dec 06 '21 at 12:41
1

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135