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);
});
});
自分の質問に答える。基本的に、コンポーネントの浅いレンダリングを作成し、認証されている場合はリダイレクトコンポーネントをレンダリングし、そうでない場合はアプリをレンダリングしていることを確認しています。ここにコード:
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);
});
});
これらの答えはどちらも私にとってはうまくいきませんでしたし、かなり掘り下げましたので、ここでの経験ではチップを使うと思いました。
_export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
auth.isAuthenticated
? <Component {...props} />
: <Redirect to={{
pathname: '/',
state: { from: props.location }
}} />
)} />
)
_
このテストは問題なく機能し、_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
を見つけるにはかなり掘り下げましたが、実際にルートに到達して条件を実行するためにこれが必要です。長い間、両方のブランチをカバーしようとして、これが必要だったことに気づきました。
これが誰かを助けることを願っています。
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");
});
RedirectApp
でRoute
をラップすることにより、MemoryRouter
は、match
にreact-router
小道具(location
、history
、およびRedirectApp
)を挿入します。
enzyme
を使用すると、これらのprops()
を取得できます。また、location
propにはリダイレクト後にpathname
が含まれるため、リダイレクトされた場所を一致させることができます。
このメソッドは少しハックですが、Redirect
が存在することだけでなく、リダイレクトが正しいの場所に行くことをテストするという利点があります。
または、RedirectApp.js
のexport default withRouter(RedirectApp)
を使用して、react-router
の小道具を自動的に取得できます。