Next.js Discord

Discord Forum

Testing component with useSearchParams

Answered
JeffBaumgardt posted this in #help-forum
Open in Discord
I have a client component that has a caller to useSearchParams. I'm using this to add query params to the route for errors

export default function Thing() {
  const searchParams = useSearchParams()
  const possibleError = searchParams.get('error')

  return (
    <div>
      {possibleError ? <span>some error</span> : null}
      <span>Stuff here</span>
    </div>
  )
}


Naive but you get the point. If the component is mounted and the uri has a query error I display an error. In practice this works. I have a component that conditionally renders an error when the search query is present.

Now, I'm trying to test this.

For starters I had to mock next/navigation just to keep TypeError: Cannot read properties of undefined (reading 'get') errors from jest.

So I have
jest.mock('next/navigation', () => {
    return {
        __esModule: true,
        useSearchParams: () => ({
            get: jest.fn(),
        }),
    }
})


But I can't for the life of me figure out how to mock the response of get in a single test.

I would like to assert the actual value of the query param as well.
So something like error=foo and error=bar

I am on Next 14 and using the app router.
Jest
React-testing-library
Typescript
Answered by JeffBaumgardt
I got it.

I changed the mock to
jest.mock('next/navigation', () => {
    return {
        __esModule: true,
        useSearchParams: jest.fn().mockImplementation(() => new URLSearchParams()),
    }
})


And in the test I did this
;(useSearchParams as jest.Mock).mockImplementation(() => new URLSearchParams('signInError=invalidSession'))


I was trying to mock the get method off of the constructor of URLSearchParams which is the actual function in useSearchParams it's just a readonly version.

So by changing the mock to mock out useSearchParams to itself being a mock implementing URLSearchParams I was able to then update the mock inside a single test.

test('Should show an error', () => {
        ;(useSearchParams as jest.Mock).mockImplementation(() => new URLSearchParams('signInError=invalidSession'))

        const {getByText} = render(<LoginModal header={<span>Testing Header</span>} />)

        expect(getByText('Invalid Session. Please login agin.')).toBeInTheDocument()
    })
View full answer

1 Reply

I got it.

I changed the mock to
jest.mock('next/navigation', () => {
    return {
        __esModule: true,
        useSearchParams: jest.fn().mockImplementation(() => new URLSearchParams()),
    }
})


And in the test I did this
;(useSearchParams as jest.Mock).mockImplementation(() => new URLSearchParams('signInError=invalidSession'))


I was trying to mock the get method off of the constructor of URLSearchParams which is the actual function in useSearchParams it's just a readonly version.

So by changing the mock to mock out useSearchParams to itself being a mock implementing URLSearchParams I was able to then update the mock inside a single test.

test('Should show an error', () => {
        ;(useSearchParams as jest.Mock).mockImplementation(() => new URLSearchParams('signInError=invalidSession'))

        const {getByText} = render(<LoginModal header={<span>Testing Header</span>} />)

        expect(getByText('Invalid Session. Please login agin.')).toBeInTheDocument()
    })
Answer