【问题标题】:How to Test Private Routing with React & React-Router如何使用 React 和 React-Router 测试私有路由
【发布时间】:2021-04-10 14:12:25
【问题描述】:

我正在尝试在我的 React 应用程序中测试私有路由。我正在使用以下版本:

"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",

在我的 PrivateRoute.js 中,我只是检查用户是否已登录并进行相应的重定向:

...
const PrivateRoute = (props) => {
  const { component: Component, ...rest } = props;
  const user = useContext(AuthContext);
  return (
    <Route
      {...rest}
      render={(matchProps) =>
        user ? <Component {...matchProps} /> : <Redirect to="/SignIn" />
      }
    />
  );
};
...

在我的 PrivateRoute.test.js 中,我只是渲染&lt;PrivateRoute&gt; 并检查history.location.pathname。但是,当我期待/SignIn 时,我总是得到/

describe("<PrivateRoute/>", () => {
  it("redirects unauthenticated users to SignIn", async () => {
    const history = createBrowserHistory();
    render(
      <BrowserRouter>
        <Switch>
          <PrivateRoute exact path="/SomeRoute" component={SomeComponent} />
        </Switch>
      </BrowserRouter>
    );

    expect(history.location.pathname).toBe("/SignIn");
  });
});

我错过了什么?

【问题讨论】:

    标签: javascript reactjs react-router react-testing-library


    【解决方案1】:

    答案是使用&lt;Router history={history}&gt;createMemoryHistory,如下所示:

    来自React Router Documentaiton

    参见下面的示例测试:

    it("redirects unauthenticated users to SignIn", async () => {
        const history = createMemoryHistory ({ initialEntries: ["/Private"] });
        const PrivateComponent = () => <>Private!</>
        const PublicComponent = () => <>Redirected!</>
        render(
            <Router history={history}>
              <Switch>
                <PrivateRoute exact path="/Private" component={PrivateComponent} />
                <Route exact path="/SignIn" component={PublicComponent} />
              </Switch>
            </Router>
        );
        expect(screen.queryByText("Private!")).not.toBeInTheDocument();
        expect(screen.queryByText("Redirected!")).toBeInTheDocument();
        expect(history.location.pathname).toBe("/SignIn")
      });
    

    【讨论】:

      【解决方案2】:

      原因可能是如果 AuthContext 不存在,您会将用户重定向到 SignIn 组件。但我认为 AuthContext 总是存在的。因此,您应该在用户登录时向上下文传递一个值。并检查该值是否存在。

      function PrivateRoute({ children, ...rest }) {
        const [loggedInUser] = useContext(AuthContext);
      
        return (
          <Route
            {...rest}
            render={({ location }) =>
              loggedInUser.isSignedIn ? (
                children
              ) : (
                <Redirect
                  to={{
                    pathname: "/SignIn",
                    state: { from: location }
                  }}
                />
              )
            }
          />
        );
      }

      您必须在 app.js 中使用 PrivateRoute 组件,就像使用 Route 组件一样。

      import React from 'react'
      import PrivateRoute from 'path/to/PrivateRoute/component'
      
      export const UserContext = createContext();
      
      export default function App() {
      const [loggedInUser, setLoggedInUser] = useState({});
      
          return (
              <UserContext.Provider value={[loggedInUser, setLoggedInUser]}>
                  <Router>
                      <Switch>
                          <PrivateRoute path="/top-secret">
                              <SecretDoor></SecretDoor>
                          </PrivateRoute>
                          <Route path="/SignIn">
                              <SignIn></SignIn>
                          </Route>
                      </Switch>
                  </Router>
              </UserContext.Provider>
          )
      }

      【讨论】:

      • &lt;Redirect to={{ pathname: "/SignIn"...&lt;Redirect to="/SignIn" 一样,不是吗?同样通过Redirect 传递state 对象似乎并没有改变任何东西。我的测试仍然收到/ 而不是/SignIn
      • 是的,它是一样的,另一个原因可能是如果AuthContext 不存在,您将被重定向到登录组件。但我认为 AuthContext 总是存在的。因此,您应该在用户登录时将值传递给上下文。并检查该值是否存在。我正在更新答案。
      • 是的,我已经这样做了。我的 PrivateRoute 完美运行。我只是在测试它时遇到了问题。
      猜你喜欢
      • 2020-08-13
      • 2018-03-05
      • 2022-01-17
      • 2021-06-14
      • 1970-01-01
      • 2018-03-06
      • 2018-01-04
      • 2022-08-12
      • 2022-07-07
      相关资源
      最近更新 更多