'How to mock Wouter in Jest

I want to mock useLocation from library wouter. based on my experience 3rd Library can be mock by following jest es6 doc.
Unfortunately, the method can be applied on wouter, still failed to mock the useLocation.

here my codesandbox snippet of Button with click will trigger setLocation

app.jsx

import React from 'react'
import {render} from 'react-dom'
import {useLocation} from 'wouter'

export default function App() {
  const [, setLocation] = useLocation()

  function clickButton() {
    console.warn('Clicked')
    setLocation('#')
  }

  return <button onClick={clickButton}>Click ME</button>
}

app.test.jsx

import React from 'react'
import {render, screen, fireEvent} from '@testing-library/react'
import App from '../index'

describe('mock useLocation', () => {
  it('should call setLocation with param `#`', () => {
    const mockLocation = jest.fn()
    const mockUseLocation = jest.fn()
    jest.spyOn(global.console, 'warn')
    jest.mock('wouter', () => {
      return function () {
        return {
          useLocation: () => [mockLocation, mockUseLocation],
        }
      }
    })

    render(<App />)
    const button = screen.getByRole('button')

    fireEvent.click(button)

    expect(console.warn).toBeCalledWith('Clicked')
    expect(mockUseLocation).toBeCalledWith('#')
  })
})

Thank you



Solution 1:[1]

I think you might have an 'extra' level of function in your jest return.

I would expect to see: jest.mock(() => ({ useLocation:... })

But you appear to have: jest.mock(() => { return function () {...

The code 'import' gets the output of the provided callback.

jest.mock(() => ({...})`
                 ^^^------ This is the 'object' that the code imports  

It's expecting an object with a useLocation property, but gets another callback.

Try:

    jest.mock('wouter', () => {
      return {
        useLocation: () => [mockLocation, mockUseLocation],
      }
    })

You might also need to move your mockLocation and mockUseLocation into the mock function. Jest will 'hoist' the mocks to the top of the file (so it can mock things before they're actually imported), which means pulling things from outside the mock can be tricky, they are often undefined when you don't expect it. This can be fixed by doing this:

    jest.spyOn(global.console, 'warn')
    jest.mock('wouter', () => {
      // Define the referenced mocks here
      const mockLocation = jest.fn()
      const mockUseLocation = jest.fn()
      return {
        useLocation: () => [mockLocation, mockUseLocation],
      }
    })

That does make getting a reference to mockLocation a real pain though, I find I have to do a:

const mockLocation = useLocation()[0];

Or, you can do:

    jest.mock('wouter', () => {
      const useLocation= jest.fn()
      return {
        useLocation
      }
    })

    const mockLocation = jest.fn()
    const mockUseLocation = jest.fn()
    useLocation.mockReturnValue([mockLocation, mockUseLocation]);

Note, that the default behaviour of a mock is exactly the above (just replaces any imported objects with a jest.fn so shorthand would be:

    jest.mock('wouter'); // by default this will mock 'useLocation'

    const mockLocation = jest.fn()
    const mockUseLocation = jest.fn()
    useLocation.mockReturnValue([mockLocation, mockUseLocation]);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1