React Router v4 Redirect unit test

后端 未结 3 1309
忘掉有多难
忘掉有多难 2021-02-04 00:50

How do I unit test the component in react router v4? I am unsuccessfully trying to unit test a simple component with a redirect using jest and enzyme.

My component:

相关标签:
3条回答
  • 2021-02-04 00:54

    Neither of these answers worked for me and took a fair bit of digging so I thought I'd chip in my experience here.

    PrivateRoute.js

    export const PrivateRoute = ({ component: Component, ...rest }) => (
      <Route {...rest} render={(props) => (
        auth.isAuthenticated
          ? <Component {...props} />
          : <Redirect to={{
            pathname: '/',
            state: { from: props.location }
          }} />
      )} />
    )
    

    PrivateRoute.spec.js

    This test worked for me with no problems whatsoever, it rendered the PrivateComponent when auth.isAuthenticated evaluated to true.

    it('renders the component when the user is authorised', () => {
      auth.login()
      expect(auth.isAuthenticated).toBe(true)
      const privateRoute = mount(
        <MemoryRouter initialEntries={['/privateComponent']}>
          <PrivateRoute path='/privateComponent' component={PrivateComponent} />
        </MemoryRouter>
      )
      expect(privateRoute.find('PrivateComponent').length).toEqual(1)
    })
    

    This was the test that gave me a lot of issues. At first I was checking for the Redirect component.

    I tried to just do something like

    expect(privateRoute.find('Redirect').length).toEqual(1)
    

    But that just wouldn't work, no matter what I did, it just couldn't find the Redirect component. In the end, I ended up checking the history but couldn't find any reliable documentation online and ended up looking at the React Router codebase.

    In MemoryRouter.js (line 30) I saw that it rendered a Router component. I noticed that it was also passing it's history as a prop to Router so I figured I would be able to grab it from there.

    I ended up grabbing the history prop from Router using privateRoute.find('Router').prop('history') which then finally gave me evidence that a redirect had actually happened, to the correct location, no less.

    it('renders a redirect when the user is not authorised', () => {
      auth.logout()
      expect(auth.isAuthenticated).toBe(false)
      const privateRoute = mount(
        <MemoryRouter initialEntries={['/privateComponent']}>
          <PrivateRoute path='/privateComponent' component={PrivateComponent} />
        </MemoryRouter>
      )
      expect(privateRoute.find('PrivateComponent').length).toEqual(0)
      expect(
        privateRoute.find('Router').prop('history').location.pathname
      ).toEqual('/')
    })
    

    With this test, you're testing the actual functionality of the PrivateRoute component and ensuring that it goes where it's saying it's going.

    The documentation leaves a lot to be desired. For example, it took a fair bit of digging for me to find out about initialEntries as a prop for MemoryRouter, you need this so it actually hits the route and executes the conditional, I spent too long trying to cover both branches only to realise this was what was needed.

    Hope this helps someone.

    0 讨论(0)
  • 2021-02-04 01:19

    Here's my minimal example of testing that the actual URL changes instead of just that a Redirect component exists on the page:

    RedirectApp.js:

    import React from "react";
    import { Route, Switch, Redirect } from "react-router-dom";
    
    const RedirectApp = props => {
      return (
        <Switch>
          <Redirect from="/all-courses" to="/courses" />
        </Switch>
      );
    };
    
    export default RedirectApp;
    

    RedirectApp.test.js:

    import React from "react";
    import { MemoryRouter, Route } from "react-router-dom";
    import { mount } from "enzyme";
    import RedirectApp from "./RedirectApp";
    
    it("redirects /all-courses to /courses", () => {
      const wrapper = mount(
        <MemoryRouter initialEntries={[`/all-courses`]}>
          <Route component={RedirectApp} />
        </MemoryRouter>
      );
    
      expect(wrapper.find(RedirectApp).props().location.pathname).toBe("/courses");
    });
    

    By wrapping RedirectApp in a Route, MemoryRouter injects the react-router props (match, location, and history) in RedirectApp.

    enzyme lets you grab these props(), and the location prop includes the pathname after redirect, so the redirected location can be matched.

    This method is a little hacky, but has the advantage of testing that a redirect is going to the correct place and not just that a Redirect exists.

    Alternatively, you can export default withRouter(RedirectApp) in RedirectApp.js to automatically get the react-router props injected.

    0 讨论(0)
  • 2021-02-04 01:20

    Answering my own question. Basically I'm making a shallow render of my component and verifying that if authenticated is rendering the redirect component otherwise the App one. Here the code:

    function setup() {
      const enzymeWrapper = shallow(<AuthenticatedApp />);
    
      return {
        enzymeWrapper
      };
    }
    
    describe("AuthenticatedApp component", () => {
      it("renders Redirect when user NOT autheticated", () => {
        authApi.isUserAuthenticated = jest.fn(() => false);
        const { enzymeWrapper } = setup();
    
        expect(enzymeWrapper.find(Redirect)).toHaveLength(1);
      });
    
      it("renders AppWithData when user autheticated", () => {
        authApi.isUserAuthenticated = jest.fn(() => true);
        const { enzymeWrapper } = setup();
    
        expect(enzymeWrapper.find(AppWithData)).toHaveLength(1);
      });
    });
    
    0 讨论(0)
提交回复
热议问题