newbie
newbie

Reputation: 600

React-Vitest: Mock useSearchParams using vitest

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 :)

Upvotes: 1

Views: 5595

Answers (3)

Chiril
Chiril

Reputation: 77

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');

Upvotes: 1

newbie
newbie

Reputation: 600

Remove the mock :

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

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

Upvotes: -1

Slava Knyazev
Slava Knyazev

Reputation: 6073

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>)

Upvotes: 0

Related Questions