web-dev-qa-db-ja.com

React Router v4 Redirect unit test

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);
  });
});
21
Luca Marangon

自分の質問に答える。基本的に、コンポーネントの浅いレンダリングを作成し、認証されている場合はリダイレクトコンポーネントをレンダリングし、そうでない場合はアプリをレンダリングしていることを確認しています。ここにコード:

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);
  });
});
20
Luca Marangon

これらの答えはどちらも私にとってはうまくいきませんでしたし、かなり掘り下げましたので、ここでの経験ではチップを使うと思いました。

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

このテストは問題なく機能し、_auth.isAuthenticated_がtrueと評価されたときにPrivateComponentをレンダリングしました。

_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 codebase。

MemoryRouter.js(30行目)Router コンポーネントをレンダリングしたことがわかりました。 historyの小道具としてRouterを渡すことにも気づいたので、そこからつかむことができると考えました。

私はRouterからprivateRoute.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を見つけるにはかなり掘り下げましたが、実際にルートに到達して条件を実行するためにこれが必要です。長い間、両方のブランチをカバーしようとして、これが必要だったことに気づきました。

これが誰かを助けることを願っています。

10
Michael Curry

Redirectコンポーネントがページに存在することだけでなく、実際のURLが変化することをテストする最小限の例を次に示します。

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

RedirectAppRouteをラップすることにより、MemoryRouterは、matchreact-router小道具(locationhistory、およびRedirectApp)を挿入します。

enzymeを使用すると、これらのprops()を取得できます。また、location propにはリダイレクト後にpathnameが含まれるため、リダイレクトされた場所を一致させることができます。

このメソッドは少しハックですが、Redirectが存在することだけでなく、リダイレクトが正しいの場所に行くことをテストするという利点があります。

または、RedirectApp.jsexport default withRouter(RedirectApp)を使用して、react-routerの小道具を自動的に取得できます。

3
cgenco