jestjs

Loose match one value in jest.toHaveBeenCalledWith


I have an analytics tracker that will only call after 1 second and with an object where the intervalInMilliseconds (duration) value is not deterministic.

How can I use jest.toHaveBeenCalledWith to test the object?

 test('pageStats - publicationPage (will wait 1000ms)', done => {
  const track = jest.fn()

  const expected = new PayloadTiming({
    category: 'PublicationPage',
    action: 'PublicationPage',
    name: 'n/a',
    label: '7',
    intervalInMilliseconds: 1000 // or around
  })

  mockInstance.viewState.layoutMode = PSPDFKit.LayoutMode.SINGLE
  const sendPageStats = pageStats({
    instance: mockInstance,
    track,
    remoteId: nappConfig.remoteId
  })

  mockInstance.addEventListener('viewState.currentPageIndex.change', sendPageStats)

  setTimeout(() => {
    mockInstance.fire('viewState.currentPageIndex.change', 2)

    expect(track).toHaveBeenCalled()
    expect(track).toHaveBeenCalledWith(expected)

    done()
  }, 1000)

  expect(track).not.toHaveBeenCalled()
})

expect(track).toHaveBeenCalledWith(expected) fails with:

Expected mock function to have been called with:
      {"action": "PublicationPage", "category": "PublicationPage", "intervalInMilliseconds": 1000, "label": "7", "name": "n/a"}
    as argument 1, but it was called with
      {"action": "PublicationPage", "category": "PublicationPage", "intervalInMilliseconds": 1001, "label": "7", "name": "n/a"}

I have looked at jest-extended but I do not see anything useful for my use-case.

EDIT: I want to highlight that all of the answers here are very useful and you can pick whichever suit your use-case. Thank you all - these answers are great!


Solution

  • This can be done with asymmetric matchers (introduced in Jest 18)

    expect(track).toHaveBeenCalledWith(
      expect.objectContaining({
       "action": "PublicationPage", 
       "category": "PublicationPage", 
       "label": "7",
       "name": "n/a"
      })
    )
    

    If you use jest-extended you can do something like

    expect(track).toHaveBeenCalledWith(
      expect.objectContaining({
       "action": "PublicationPage", 
       "category": "PublicationPage", 
       "label": "7",
       "name": "n/a",
       "intervalInMilliseconds": expect.toBeWithin(999, 1002)
      })
    )