Search code examples
javascriptreactjsunit-testingvitest

No "BrowserRouter" export is defined on the "/node_modules/react-router-dom/dist/main.js" mock. Did you forget to return it from "vi.mock"?


I'm testing a simple React component using Vitest. This is the function that renders my component: (the server stuff is MSW)

function renderComponent() {
  render(<Router initialEntries={['/entry-detail']}> <SnackbarProvider>
    <EntryDetail />
  </SnackbarProvider>
  </Router>
  );
}

This is my test:

describe('EntryDetail component', () => {
  beforeAll(() => server.listen());
  afterEach(() => server.resetHandlers());
  afterAll(() => server.close());

  vi.mock('react-router-dom', () => ({
    useParams: () => ({
      articleId: '63d466ca3d00b50db15aed93',
    }),
  }));

  it('allows only 150 characters for comments', async () => {
    authorizedServerSetup();
    renderComponent();
    // const commentInput = await screen.findByRole('textbox');
    // console.log(commentInput);
  });
});

This is the error message that I get which I don't understand: enter image description here

Error: [vitest] No "BrowserRouter" export is defined on the "/node_modules/react-router-dom/dist/main.js" mock. Did you forget to return it from "vi.mock"?

Following the advice on the console didn't make any difference. I tried using MemoryRouter as well, no difference. What's going on? How do I test this?

The component to be tested is the following:

export const EntryDetail = () => {
  const { articleId } = useParams();
  const [article, setArticle] = useState({ title: null, body: null, comments: [], likes: [] });
  const { title, body, comments, likes, image } = article;

  useEffect(() => {
    (async () => {
      try {
        const response = await getArticleDetail(articleId);
        const { title, body, comments, likes, image } = response.data;
        setArticle({ title, body, comments, likes, image });
      } catch (e) {
        console.error(e);
      }
    })();
  }, []);

  return (
    <Container>
      {
        !article.title
          ? <div style={{ textAlign: 'center' }}>Loading...</div>
          : <>
            <Title>{title}</Title>
            <img src={image} />
            <p className="body">{body}</p>
            <ReactionBar {...{ likes, articleId }} />
            <CommentsSection {...{ comments, articleId }} />
          </>
      }

    </Container>
  );
};

I also tried something like this:

vi.mock('react-router-dom', async (importOriginal) => {
  const mod = await importOriginal();
  return {
    ...mod,
    useParams: () => ({
      articleId: '63d466ca3d00b50db15aed93',
    }),
  };
});

But I'm getting basically the same error: enter image description here


Solution

  • For anyone else struggling with this, the issue is that you need to mock the BrowserRouter in addition to the useParams hook. To do that, you need to first import BrowserRouter from 'react-router-dom'

    import { BrowserRouter as Router } from "react-router-dom";
    

    And then add it to the callback of the vi.mock method like so:

     vi.mock('react-router-dom', () => ({
        useParams: () => ({
          articleId: '63d466ca3d00b50db15aed93',
        }),
        BrowserRouter: vi.fn().mockImplementation((props) => props.children),
      }));
    

    Then you can use your component in a test as you normally would :)