reactjstypescriptreact-routerreact-testing-libraryredux-toolkit

Using "React Testing library" - How to wrap all my test component with providers


I'm using React-Testing-Library and I have my main.tsx file like this with all the Redux-Toolkit and React-Router wrappers as follows:

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <Provider store={store}>
        <Suspense fallback={<LoadingSpinner />}>
          <BrowserRouter>
            <Routes>
              <Route path="/*" element={<App />} />
            </Routes>
          </BrowserRouter>
        </Suspense>
    </Provider>
  </React.StrictMode>,
);

Now, when I try to test any component that uses any useDispatch or useNavigate the test fails unless I wrap that component with the providers like this:

test("Inputs should be initially empty", () => {
  render(
    <Provider store={store}>
      <BrowserRouter>
        <Login />
      </BrowserRouter>
      ,
    </Provider>,
  );
  const emailInput: HTMLInputElement = screen.getByRole("textbox", {
    name: /email/i,
  });
  expect(emailInput.value).toBe("");
});

Now, the test has passed and everything is good.

My question is, is there a way to initially wrap all the components I'm going to test by default with these wrappers? So that I don't have to wrap all my test components with these every time?


Solution

  • Create a wrapper component that renders any providers and wraps the children prop.

    const ProviderWrapper = ({ children }) => (
      <Provider store={store}>
        <BrowserRouter>
          {children}
        </BrowserRouter>
      </Provider>
    );
    
    test("Inputs should be initially empty", () => {
      render(<Login />, { wrapper: ProviderWrapper });
    
      const emailInput: HTMLInputElement = screen.getByRole("textbox", {
        name: /email/i,
      });
    
      expect(emailInput.value).toBe("");
    });
    

    If you just need to wrap every component you test with the same providers then you can also create a custom render function.

    Example:

    import {render} from '@testing-library/react';
    
    const ProviderWrapper = ({ children }) => (
      <Provider store={store}>
        <BrowserRouter>
          {children}
        </BrowserRouter>
      </Provider>
    );
    
    const customRender = (ui, options) =>
      render(ui, { wrapper: ProviderWrapper, ...options });
    
    // re-export everything
    export * from '@testing-library/react';
    
    // override render method
    export { customRender as render };
    
    test("Inputs should be initially empty", () => {
      render(<Login />); // <-- custom render already wraps with providers
    
      const emailInput: HTMLInputElement = screen.getByRole("textbox", {
        name: /email/i,
      });
    
      expect(emailInput.value).toBe("");
    });