React Router v4重定向单元测试

时间:2017-06-01 14:07:30

标签: unit-testing reactjs 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 个答案:

答案 0 :(得分:18)

回答我自己的问题。 基本上,我正在对我的组件进行浅层渲染,并验证如果经过身份验证正在呈现重定向组件,那么另一个是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);
  });
});

答案 1 :(得分:6)

这两个答案都不适合我,也需要大量的挖掘工作,因此我认为我会借鉴我的经验。

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

该测试对我没有任何问题,当PrivateComponent评估为true时,它呈现了auth.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 (line 30)中,我看到它呈现了一个Router组件。我注意到它也将history作为对Router的支持,所以我认为我可以从那里抓住它。

我最终使用RouterprivateRoute.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作为MemoryRouter的道具,您需要它,所以它实际上符合条件并执行有条件,我花了很长时间尝试覆盖两个分支只是意识到这是需要的。

希望这对某人有帮助。

答案 2 :(得分:2)

以下是我测试实际网址更改的最小示例,而不仅仅是页面上存在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包裹在Route中,MemoryRouter会注入react-router道具(matchlocationhistoryRedirectApp

enzyme可让您抓取这些props()location道具包含重定向后的pathname,因此可以匹配重定向的位置。

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

或者,您可以export default withRouter(RedirectApp) RedirectApp.js自动获取react-router道具注入。