IT이야기

반응 컨텍스트 및 후크 API의 효소 오류

cyworld 2022. 4. 4. 21:05
반응형

반응 컨텍스트 및 후크 API의 효소 오류

내 작은 리액션 후크 앱의 인증을 처리하기 위해 이 루트 컨텍스트를 만들었다.효소세포를 이용해 이상한 실수를 하는 것 외에는 모든 것이 예상대로 작동한다.shallow그리고mount.

이렇게 시험해 보려고 한다.

const wrapper = mount(<Login />)

색인:

import RootContext from './RootContext'

function Root() {
  return (
    <RootContext>
      <App />
    </RootContext>
  )
}

ReactDOM.render(<Root/>, document.getElementById('root'));

루트 컨텍스트:

import React, { useEffect, useState } from 'react'
export const RootContext = React.createContext()

export default ({ children }) => {
  const auth = window.localStorage.getItem('authenticated') || 'false'
  const cred = window.localStorage.getItem('credentials') || null
  const [authenticated, setAuthenticated] = useState(auth)
  const [credentials, setCredentials] = useState(cred)

  useEffect(
    () => {
      window.localStorage.setItem('authenticated', authenticated)
      window.localStorage.setItem('credentials', credentials)
    },
    [authenticated, credentials]
  )

  const defaultContext = {
    authenticated,
    setAuthenticated,
    credentials,
    setCredentials 
  }

  return (
    <RootContext.Provider value={defaultContext}>
      {children}
    </RootContext.Provider>
  )
}

로그인, 로그아웃 및 등록 모두 사용useAuthenticate이 문제를 일으키는 고리를 걸다BmiForm 구성품은 잘 작동한다.

import AuthenticatedRoute from './AuthenticatedRoute'

export default function App() {

  return (
    <Router>
      <Header />
      <Switch>
        <Container>
          <Row>
            <Col md={{ span: 4, offset: 4 }}>
              <AuthenticatedRoute exact path="/" component={BmiForm} />
              <Route exact path="/login" component={ Login } />
              <Route exact path="/logout" component={ Logout } />
              <Route exact path="/register" component={ Register } />
            </Col>
          </Row>
        </Container>
      </Switch>
    </Router>
  )
}

useAuthenticate훅이 문제를 일으키고 있다.

import useReactRouter from 'use-react-router';
import { RootContext } from './../RootContext'

export default function useAuthenticate() {
  const { history } = useReactRouter()
  const {
    authenticated,
    setAuthenticated,
    credentials,
    setCredentials
  } = useContext(RootContext);

추가하기useAuthenticateBmiForm에 연결하면 동일한 방법으로 테스트가 실패하게 된다.

import useAuthenticate from './custom/useAuthenticate'

export default function BmiForm(props) {
  const { credentials, setAuthenticated } = useAuthenticate()

첫 번째 오류:

    TypeError: Cannot read property 'authenticated' of undefined

       5 | export default function useAuthenticate() {
       6 |   const {
    >  7 |     authenticated,
         |     ^
       8 |     setAuthenticated,
       9 |     credentials,
      10 |     setCredentials

스택 추적의 두 번째 오류:

   use-react-router may only be used within a react-router context.

      4 | 
      5 | export default function useAuthenticate() {
    > 6 |   const { history } = useReactRouter()
        |                       ^
      7 |   const {
      8 |     authenticated,
      9 |     setAuthenticated,

      at useRouter (node_modules/use-react-router/src/use-react-router.ts:20:11)
      at useAuthenticate (src/custom/useAuthenticate.js:6:23)
      at BmiForm (src/BmiForm.js:15:45)
      at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:12839:18)
      at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:14816:13)
      at beginWork (node_modules/react-dom/cjs/react-dom.development.js:15421:16)
      at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:19108:12)
      at workLoop (node_modules/react-dom/cjs/react-dom.development.js:19148:24)
      at renderRoot (node_modules/react-dom/cjs/react-dom.development.js:19231:7)
      at performWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:20138:7)
      at performWork (node_modules/react-dom/cjs/react-dom.development.js:20050:7)
      at performSyncWork (node_modules/react-dom/cjs/react-dom.development.js:20024:3)
      at requestWork (node_modules/react-dom/cjs/react-dom.development.js:19893:5)
      at scheduleWork (node_modules/react-dom/cjs/react-dom.development.js:19707:5)
      at scheduleRootUpdate (node_modules/react-dom/cjs/react-dom.development.js:20368:3)
      at updateContainerAtExpirationTime (node_modules/react-dom/cjs/react-dom.development.js:20396:10)
      at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:20453:10)
      at ReactRoot.Object.<anonymous>.ReactRoot.render (node_modules/react-dom/cjs/react-dom.development.js:20749:3)
      at node_modules/react-dom/cjs/react-dom.development.js:20886:14
      at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:20255:10)
      at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:20882:5)
      at Object.render (node_modules/react-dom/cjs/react-dom.development.js:20951:12)
      at Object.render (node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:382:114)
      at new ReactWrapper (node_modules/enzyme/build/ReactWrapper.js:134:16)
      at mount (node_modules/enzyme/build/mount.js:21:10)
      at test (src/test/bmi_calculator.step.test.js:22:21)
      at defineScenarioFunction (node_modules/jest-cucumber/src/feature-definition-creation.ts:155:9)
      at test (src/test/bmi_calculator.step.test.js:20:3)
      at Suite.<anonymous> (node_modules/jest-cucumber/src/feature-definition-creation.ts:279:9)
      at defineFeature (node_modules/jest-cucumber/src/feature-definition-creation.ts:278:5)
      at Object.<anonymous> (src/test/bmi_calculator.step.test.js:19:1)

효소의 효능과 관련된 다양한 해결책들을 시도해 보았다.setContext그러나 이것이 컨텍스트와 관련이 있는지 없는지 확실하지 않다.react-router또는 둘 다.

테스트하는 거니까.context, 이상적으로는 루트 수준에서 테스트하고 거기서부터 모든 DOM 변경에 대해 주장을 하고 싶을 것이다.또한, 사용할 수 없다는 점도 유의하십시오.Route라우터 외부(BrowserRouter,Router,StaticRouter, ...등)도 아니다.history라우터에 연결되지 않은.비록 한 번도 사용해 본 적이 없지만use-react-router후드 아래를 들여다보면, 여전히 라우터가 필요하다.따라서, 당신의 테스트는 다음 사항을 포함해야 한다.Provider, 라우터 및 페이지/구성 요소.

루트 레벨에서 테스트하는 작업:

보호된 경로 루트 컨텍스트 편집

src/root/index.js

import React from "react";
import { Provider } from "../hooks/useAuthentication";
import Routes from "../routes";

const Root = () => (
  <Provider>
    <Routes />
  </Provider>
);

export default Root;

src/sshd/index.js

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";

import { Container, Header, ProtectedRoutes } from "../components";
import { About, Dashboard, Home } from "../pages";

const Routes = () => (
  <BrowserRouter>
    <Container>
      <Header />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/about" component={About} />
        <ProtectedRoutes>
          <Route exact path="/dashboard" component={Dashboard} />
        </ProtectedRoutes>
      </Switch>
    </Container>
  </BrowserRouter>
);

export default Routes;

src/root/__viron__/root.test.js

import React from "react";
import { mount } from "enzyme";
import Root from "../index";

describe("Authentication", () => {
  let wrapper;
  beforeAll(() => {
    wrapper = mount(<Root />);
    wrapper
      .find("Router")
      .prop("history")
      .push("/dashboard");
    wrapper.update();
  });

  afterAll(() => {
    wrapper.unmount();
  });

  it("initially renders a Login component and displays a message", () => {
    expect(wrapper.find("h1").text()).toEqual("Login");
    expect(wrapper.find("h3").text()).toEqual(
      "You must login before viewing the dashboard!"
    );
  });

  it("authenticates the user and renders the Dashboard", () => {
    wrapper.find("button").simulate("click");

    expect(wrapper.find("h1").text()).toEqual("Dashboard");
  });

  it("unauthenticates the user and redirects the user to the home page", () => {
    wrapper.find("button").simulate("click");
    expect(wrapper.find("h1").text()).toEqual("Home");
  });
});

Dashboard 페이지는 인증 기능에 대한 액세스 권한이 있는 한 격리될 수 있지만, 이는 후속 페이지/구성 요소에 대한 몇 가지 반복적인 테스트 사례를 만들 수 있으며, 여전히 컨텍스트를 루트 레벨과 라우터(특히 구성 요소/페이지 또는 후크가 구독 중인 경우)로 설정해야 하기 때문에 그다지 타당하지 않다.history).

테스트하기 위해 대시보드 페이지가 분리되어 있는 작업 예:

보호된 경로 제한 컨텍스트 편집

src/sshd/index.js

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";

import { Container, Header, ProtectedRoutes } from "../components";
import { About, Dashboard, Home } from "../pages";

const Routes = () => (
  <BrowserRouter>
    <Container>
      <Header />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/about" component={About} />
        <ProtectedRoutes>
          <Route exact path="/dashboard" component={Dashboard} />
        </ProtectedRoutes>
      </Switch>
    </Container>
  </BrowserRouter>
);

export default Routes;

구성 요소/ProtectedRoutes/index.js

import React from "react";
import { useAuthentication } from "../../hooks";
import Login from "../Login";

const ProtectedRoutes = ({ children }) => {
  const { isAuthenticated, login } = useAuthentication();

  return isAuthenticated ? children : <Login login={login} />;
};

export default ProtectedRoutes;

페이지/대시보드/index.js

import React, { Fragment, useCallback } from "react";
import { useAuthentication } from "../../hooks";
import { Button, Description, Title } from "../../components";

const Dashboard = ({ history }) => {
  const { logout } = useAuthentication();

  const unAuthUser = useCallback(() => {
    logout();
    history.push("/");
  }, [history, logout]);

  return (
    <Fragment>
      <Title>Dashboard</Title>
      <Description>
        Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper
        suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem
        vel eum iriure dolor in hendrerit in vulputate velit esse molestie
        consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et
        accumsan et iusto odio dignissim qui blandit praesent luptatum zzril
        delenit augue duis dolore te feugait nulla facilisi.
      </Description>
      <Button onClick={unAuthUser}>Logout</Button>
    </Fragment>
  );
};

export default Dashboard;

페이지/대시보드/_tests__/대시보드.test.js

import React from "react";
import { mount } from "enzyme";
import { BrowserRouter, Route } from "react-router-dom";
import { Provider } from "../../../hooks/useAuthentication";
import { ProtectedRoutes } from "../../../components";
import Dashboard from "../index";

describe("Dashboard Page", () => {
  let wrapper;
  beforeAll(() => {
    wrapper = mount(
      <Provider>
        <BrowserRouter>
          <ProtectedRoutes>
            <Route exact path="/" component={Dashboard} />
          </ProtectedRoutes>
        </BrowserRouter>
      </Provider>
    );
  });

  afterAll(() => {
    wrapper.unmount();
  });

  it("initially renders a login component and displays a message", () => {
    expect(wrapper.find("h1").text()).toEqual("Login");
    expect(wrapper.find("h3").text()).toEqual(
      "You must login before viewing the dashboard!"
    );
  });

  it("authenticates the user and updates the component", () => {
    wrapper.find("button").simulate("click");

    expect(wrapper.find("h1").text()).toEqual("Dashboard");
  });

  it("unauthenticates the user", () => {
    wrapper.find("button").simulate("click");
    expect(wrapper.find("h1").text()).toEqual("Login");
  });
});

참조URL: https://stackoverflow.com/questions/54974634/enzyme-errors-with-react-context-and-hooks-api

반응형