dan*_*son 5 reactjs enzyme react-router-v4 react-context react-hooks
我已经创建了此RootContext来处理我的小型React Hooks应用程序的身份验证。一切正常,除了使用Enzyme shallow和产生奇怪的错误mount。
我正在尝试像这样测试它:
const wrapper = mount(<Login />)
Run Code Online (Sandbox Code Playgroud)
指数:
import RootContext from './RootContext'
function Root() {
return (
<RootContext>
<App />
</RootContext>
)
}
ReactDOM.render(<Root/>, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)
RootContext:
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>
)
}
Run Code Online (Sandbox Code Playgroud)
登录,注销和注册全部使用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>
)
}
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
将useAuthenticate钩子添加到BmiForm会导致其测试以相同的方式失败。
import useAuthenticate from './custom/useAuthenticate'
export default function BmiForm(props) {
const { credentials, setAuthenticated } = useAuthenticate()
Run Code Online (Sandbox Code Playgroud)
我得到的第一个错误:
TypeError: Cannot read property 'authenticated' of undefined
5 | export default function useAuthenticate() {
6 | const {
> 7 | authenticated,
| ^
8 | setAuthenticated,
9 | credentials,
10 | setCredentials
Run Code Online (Sandbox Code Playgroud)
stacktrace的第二个错误:
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)
Run Code Online (Sandbox Code Playgroud)
我已经尝试过各种涉及酶的解决方案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;
Run Code Online (Sandbox Code Playgroud)
src/routes/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;
Run Code Online (Sandbox Code Playgroud)
src/root/__tests__/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");
});
});
Run Code Online (Sandbox Code Playgroud)
Dashboard页面只要能访问认证功能即可隔离;但是,这可能会为后续页面/组件创建一些重复的测试用例,并且没有多大意义,因为它仍然需要在根级别和路由器上设置上下文(特别是如果组件/页面或挂钩正在订阅)history)。
这是一个工作示例,其中仪表板页面已被隔离以进行测试:
src/routes/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;
Run Code Online (Sandbox Code Playgroud)
组件/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;
Run Code Online (Sandbox Code Playgroud)
页面/仪表板/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;
Run Code Online (Sandbox Code Playgroud)
页面/仪表板/__tests__/Dashboard.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");
});
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
934 次 |
| 最近记录: |