React Router v4 重定向单元测试

IT技术 unit-testing reactjs react-router
2021-05-20 11:51:49

如何在 react router v4 中对组件进行单元测试?我尝试使用 jest 和酶对带有重定向的简单组件进行单元测试,但未成功。

我的组件:

 const AppContainer = ({ location }) =>
  (isUserAuthenticated()
    ? <AppWithData />
    : <Redirect
        to={{
          pathname: "/login",
          state: { from: location }
        }}
      />);

我尝试测试它:

  function setup() {
      const enzymeWrapper = mount(
        <MemoryRouter initialEntries={["/"]}>
          <AppContainer />
        </MemoryRouter>
      );

      return {
        enzymeWrapper
      };
    }

    jest.mock("lib/authAPI", () => ({
      isUserAuthenticated: jest.fn(() => false)
    }));

    describe("AppContainer component", () => {
      it("renders redirect", () => {
        const { enzymeWrapper } = setup();

        expect(enzymeWrapper.find("<Redirect></Redirect>")).toBe(true);
      });
    });
3个回答

回答我自己的问题。基本上,我正在对我的组件进行浅层渲染,并验证是否经过身份验证正在渲染重定向组件,否则是 App 组件。这里的代码:

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

这些答案都不适合我,并进行了大量挖掘,所以我想我会在这里积累经验。

私有路由.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

这个测试对我来说没有任何问题,它使PrivateComponentwhenauth.isAuthenticated评估为真。

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

这是给我带来很多问题的测试。起初我正在检查Redirect组件。

我试着做类似的事情

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

但这不起作用,无论我做什么,它都找不到Redirect组件。最后,我最终查看了历史记录,但在网上找不到任何可靠的文档,并最终查看了 React Router 代码库。

MemoryRouter.js(第 30 行)中,我看到它呈现了一个Router组件。我注意到它也将它history作为props传递给,Router所以我想我可以从那里抓住它。

我最终从Router使用中获取了历史propsprivateRoute.find('Router').prop('history'),然后最终给了我证据证明重定向确实发生了,到了正确的位置,不少于。

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('/')
})

通过此测试,您正在测试PrivateRoute组件的实际功能,并确保它按预期运行。

该文档还有很多不足之处。例如,我花了相当多的时间才找到关于initialEntries的propsMemoryRouter,你需要这个所以它实际上命中了路线并执行了条件,我花了太长时间试图覆盖两个分支才意识到这是什么需要。

希望这可以帮助某人。

这是我测试实际 URL 更改而不仅仅是Redirect页面上存在组件的最小示例

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");
});

通过包裹RedirectApp在 a 中RouteMemoryRouterreact-routerprops ( match, location, and history)注入RedirectApp.

enzyme让你抓住这些props()locationprops包括pathname后重定向,所以重定向的位置可以匹配。

这种方法有点笨拙,但具有测试重定向是否到达正确位置而不仅仅是Redirect存在的优点

另外,您也可以export default withRouter(RedirectApp)RedirectApp.js自动获取到react-router的props注入。