skip to Main Content

I am trying to mock useSearchParams using vitest. I have tried a couple of methods but couldn’t find a solution.

The sample code can be found here:
https://codesandbox.io/p/sandbox/laughing-bas-m7ygo8

import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";

function App() {
  const [searchParams, setSearchParams] = useSearchParams();
  const [search, setSearch] = useState("");
  const [direction, setDirection] = useState("");

  const sortParams = searchParams.get("sortBy");

  useEffect(() => {
    setDirection(sortParams || "");
  }, [sortParams]);

  const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const val = event.target.value;
    setSearch(val);
    setSearchParams((prev) => {
      prev.set("search", val);
      return prev;
    });
  };

  const handleSortChange = (val: string) => {
    setDirection(val);
    setSearchParams((prev) => {
      prev.set("sortBy", val);
      return prev;
    });
  };

  return (
    <div>
      <p>Hello</p>
      <input type="search" value={search} onChange={handleSearchChange} />
      <button onClick={() => handleSortChange("asc")}>Sort</button>
      {direction}
    </div>
  );
}

export default App;
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { useSearchParams } from "react-router-dom";
import App from "./App";

vi.mock("react-router-dom", () => ({
  useSearchParams: vi.fn(),
}));

describe("Simple working test", () => {
  it("url should update when user clicks sort button", () => {
    render(<App />);
    const button = screen.getByRole("button");
    userEvent.click(button);
    expect(screen.getByText(/Hello/i)).toBeInTheDocument();
  });
});

Can anyone please help me here? Thanks 🙂

3

Answers


  1. Chosen as BEST ANSWER

    Remove the mock :

    vi.mock("react-router-dom", () => ({
      useSearchParams: vi.fn(),
    }));
    

    Instead use: render(<App />, { wrapper: BrowserRouter });


  2. You should not mock useSearchParams. Instead, wrap your component in a MemoryRouter for testing:

        render(<MemoryRouter initialEntries={['blogs/1']}>
          <Route path='blogs/:blogId'>
            <App />
          </Route>
        </MemoryRouter>)
    
    Login or Signup to reply.
  3. If you know what you doing and there is no other way for you to test it, try to mock it like this:

        const mockUseSearchParams = vi.fn();
    
        vi.mock('react-router-dom', async () => {
          const actual = await vi.importActual('react-router-dom') as any;
          return {
            ...actual,
            useSearchParams: () => mockUseSearchParams
          };    
        });
    

    And then you can test against mockUseSearchParams.
    For example:

    expect(mockUseSearchParams).toHaveBeenCalledWith('some_value_you_want_to_test');
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search