失败测试:渲染没有返回任何内容。相同的测试通过,与失败测试的唯一区别是使用 initialEntries
Failing Test: Nothing was returned from render. identical test passes, only difference with the failing test is the use of initialEntries
我正在尝试测试我的路由是否正常工作,当我测试默认的“/”路由时,第一个测试通过了,但是第二个测试没有通过。唯一的区别是在第二个测试中引入了 initialEntries={['/register']}
,因此第二个测试失败是因为这个原因:
"Login(...): Nothing was returned from render. This usually means a
return statement is missing. Or, to render nothing, return null."
我已经检查了与此相关的其他问题,但它们似乎与我的特定问题不符
这是我的app.js,包含所有路线
import React from 'react';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import PrivateRoute from './components/PrivateRoute';
import Login from './components/LoginRegister/Login';
import Dashboard from './components/Dashboard/Dashboard';
import Register from './components/LoginRegister/Register';
import PageNotFound from './components/Pages/PageNotFound';
import Header from './components/LoginRegister/Header';
function App() {
return (
<Router>
<div className="main-container">
<div className="Route-container">
<Header/>
<Switch>
<PrivateRoute exact path = '/dashboard' component = {Dashboard}></PrivateRoute>
<Route exact path = '/' component = {Login} />
<Route exact path = '/test' component = {Dashboard} />
<Route exact path = '/register' component = {Register} />
<Route component = {PageNotFound} />
</Switch>
</div>
</div>
</Router>
);
}
export default App;
这是我的App.test.js
import React from 'react'
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import App from "./App";
import Dashboard from './components/Dashboard/Dashboard';
import Header from './components/LoginRegister/Header';
import Login from './components/LoginRegister/Login';
import Register from './components/LoginRegister/Register';
import PageNotFound from './components/Pages/PageNotFound';
import '@testing-library/jest-dom/extend-expect';
jest.mock('./components/Dashboard/Dashboard');
jest.mock('./components/LoginRegister/Login');
jest.mock('./components/LoginRegister/Register');
jest.mock('./components/Pages/PageNotFound');
jest.mock('./components/LoginRegister/Header');
describe("App component", () =>{
it("renders page header and login component on default route", ()=>{
Header.mockImplementation(()=> <div>HeaderMock</div>);
Login.mockImplementation(()=> <div>LoginMock</div>);
render(
<MemoryRouter>
<App />
</MemoryRouter>
);
expect(screen.getByText("HeaderMock")).toBeInTheDocument();
expect(screen.getByText("LoginMock")).toBeInTheDocument();
});
it("Renders page header and Register component on Register route", ()=>{
Header.mockImplementation(()=> <div>HeaderMock</div>);
Register.mockImplementation(()=> <div>RegisterMock</div>);
render(
<MemoryRouter initialEntries={['/register']}>
<App />
</MemoryRouter>
);
expect(screen.getByText("HeaderMock")).toBeInTheDocument();
expect(screen.getByText("RegisterMock")).toBeInTheDocument();
});
});
这是 Register.js 组件
import React from 'react'
import { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
const initialState = {
credentials: {
name: '',
username: '',
password: ''
}
}
function Register() {
const [state, setState] = useState(initialState)
const history = useHistory()
function goToLogin () {
history.push('/')
}
function handleChange(e) {
setState({
credentials: {
...state.credentials,
[e.target.name]: e.target.value
}
})
}
return (
<div className = "main-container">
<div className = "middle-section">
<div className= "login-section">
<div className = "selection login-flex-item">
Already a member?
<Link to ='/' className = 'select-logreg'>Login</Link>
</div>
<form className = 'login-flex-item' onSubmit = {goToLogin}>
<input className = 'form-item'
type = 'text'
name = 'name'
placeholder = "Enter Name"
value = {state.credentials.name}
onChange ={handleChange}
/>
<input className = 'form-item'
type = 'text'
name = 'username'
placeholder = "Username"
value = {state.credentials.username}
onChange ={handleChange}
/>
<input className = 'form-item'
type="password"
name="password"
placeholder = "Password"
value={state.credentials.password}
onChange={handleChange}
/>
<button className = 'form-item btn'>Sign Up</button>
</form>
</div>
</div>
</div>
);
};
export default Register;
这是测试失败的描述我得到任何帮助都很棒我已经盯着这个看了好几个小时但似乎找不到解决方案。
FAIL src/App.test.js
App component
√ renders page header and login component on default route (33 ms)
× Renders page header and Register component on Register route (198 ms)
● App component › Renders page header and Register component on Register route
Login(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
35 | Register.mockImplementation(()=> <div>RegisterMock</div>);
36 |
> 37 | render(
| ^
38 | <MemoryRouter initialEntries={['/register']}>
39 | <App />
40 | </MemoryRouter>
at reconcileChildFibers (node_modules/react-dom/cjs/react-dom.development.js:14169:23)
at reconcileChildren (node_modules/react-dom/cjs/react-dom.development.js:16990:28)
at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17890:5)
at beginWork (node_modules/react-dom/cjs/react-dom.development.js:19049:16)
at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:3945:14)
at HTMLUnknownElement.callTheUserObjectsOperation (node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:338:25)
at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:3994:16)
at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:4056:31)
at beginWork (node_modules/react-dom/cjs/react-dom.development.js:23964:7)
at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:22779:12)
at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:22707:5)
at renderRootSync (node_modules/react-dom/cjs/react-dom.development.js:22670:7)
at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:22293:18)
at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21881:7)
at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:25482:3)
at node_modules/react-dom/cjs/react-dom.development.js:26021:7
at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:22431:12)
at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:26020:5)
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:26103:10)
at node_modules/@testing-library/react/dist/pure.js:101:25
at batchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:22380:12)
at act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:1042:14)
at render (node_modules/@testing-library/react/dist/pure.js:97:26)
at Object.<anonymous> (src/App.test.js:37:7)
来自文档 <Router>
:
The common low-level interface for all router components. Typically apps will use one of the high-level routers instead:
<BrowserRouter>
<HashRouter>
<MemoryRouter>
<NativeRouter>
<StaticRouter>
问题是 <BrowserRouter>
组件像这样包装在 <MemoryRouter>
中:
<MemoryRouter>
<BrowserRouter>
<Switch>
....
</Switch>
</BrowserRouter>
</MemoryRouter>
这将为您提供来自 <BrowserRouter>
的 location
,它将是:
location: { pathname: '/', search: '', hash: '', state: undefined }
您应该将 <Switch>
及其子项拆分为一个独立的组件,例如 Routes
:
import React from 'react';
import { BrowserRouter as Router, Route, Switch, useLocation } from 'react-router-dom';
const Login = () => <div>login</div>;
const Register = () => <div>Register</div>;
export const Routes = () => {
const location = useLocation();
console.log('location: ', location);
return (
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/register" component={Register} />
</Switch>
);
};
function App() {
return (
<Router>
<Routes />
</Router>
);
}
export default App;
以便您可以使用 Routes
和 <MemoryRouter>
进行测试:
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { Routes } from './app';
describe('App component', () => {
it('renders page header and login component on default route', () => {
render(
<MemoryRouter>
<Routes />
</MemoryRouter>
);
expect(screen.getByText('login')).toBeInTheDocument();
});
it('Renders page header and Register component on Register route', () => {
render(
<MemoryRouter initialEntries={['/register']}>
<Routes />
</MemoryRouter>
);
expect(screen.getByText('Register')).toBeInTheDocument();
});
});
测试结果:
PASS examples/70090905/app.test.tsx (8.087 s)
App component
✓ renders page header and login component on default route (45 ms)
✓ Renders page header and Register component on Register route (3 ms)
console.log
location: {
pathname: '/',
search: '',
hash: '',
state: undefined,
key: 'zlnxcn'
}
at Routes (examples/70090905/app.tsx:9:11)
console.log
location: {
pathname: '/register',
search: '',
hash: '',
state: undefined,
key: '8z804n'
}
at Routes (examples/70090905/app.tsx:9:11)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.625 s, estimated 11 s
出于说明和演示目的,我删除了不必要的代码。
包版本:
"react-router-dom": "^5.2.0"
我正在尝试测试我的路由是否正常工作,当我测试默认的“/”路由时,第一个测试通过了,但是第二个测试没有通过。唯一的区别是在第二个测试中引入了 initialEntries={['/register']}
,因此第二个测试失败是因为这个原因:
"Login(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null."
我已经检查了与此相关的其他问题,但它们似乎与我的特定问题不符
这是我的app.js,包含所有路线
import React from 'react';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import PrivateRoute from './components/PrivateRoute';
import Login from './components/LoginRegister/Login';
import Dashboard from './components/Dashboard/Dashboard';
import Register from './components/LoginRegister/Register';
import PageNotFound from './components/Pages/PageNotFound';
import Header from './components/LoginRegister/Header';
function App() {
return (
<Router>
<div className="main-container">
<div className="Route-container">
<Header/>
<Switch>
<PrivateRoute exact path = '/dashboard' component = {Dashboard}></PrivateRoute>
<Route exact path = '/' component = {Login} />
<Route exact path = '/test' component = {Dashboard} />
<Route exact path = '/register' component = {Register} />
<Route component = {PageNotFound} />
</Switch>
</div>
</div>
</Router>
);
}
export default App;
这是我的App.test.js
import React from 'react'
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import App from "./App";
import Dashboard from './components/Dashboard/Dashboard';
import Header from './components/LoginRegister/Header';
import Login from './components/LoginRegister/Login';
import Register from './components/LoginRegister/Register';
import PageNotFound from './components/Pages/PageNotFound';
import '@testing-library/jest-dom/extend-expect';
jest.mock('./components/Dashboard/Dashboard');
jest.mock('./components/LoginRegister/Login');
jest.mock('./components/LoginRegister/Register');
jest.mock('./components/Pages/PageNotFound');
jest.mock('./components/LoginRegister/Header');
describe("App component", () =>{
it("renders page header and login component on default route", ()=>{
Header.mockImplementation(()=> <div>HeaderMock</div>);
Login.mockImplementation(()=> <div>LoginMock</div>);
render(
<MemoryRouter>
<App />
</MemoryRouter>
);
expect(screen.getByText("HeaderMock")).toBeInTheDocument();
expect(screen.getByText("LoginMock")).toBeInTheDocument();
});
it("Renders page header and Register component on Register route", ()=>{
Header.mockImplementation(()=> <div>HeaderMock</div>);
Register.mockImplementation(()=> <div>RegisterMock</div>);
render(
<MemoryRouter initialEntries={['/register']}>
<App />
</MemoryRouter>
);
expect(screen.getByText("HeaderMock")).toBeInTheDocument();
expect(screen.getByText("RegisterMock")).toBeInTheDocument();
});
});
这是 Register.js 组件
import React from 'react'
import { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
const initialState = {
credentials: {
name: '',
username: '',
password: ''
}
}
function Register() {
const [state, setState] = useState(initialState)
const history = useHistory()
function goToLogin () {
history.push('/')
}
function handleChange(e) {
setState({
credentials: {
...state.credentials,
[e.target.name]: e.target.value
}
})
}
return (
<div className = "main-container">
<div className = "middle-section">
<div className= "login-section">
<div className = "selection login-flex-item">
Already a member?
<Link to ='/' className = 'select-logreg'>Login</Link>
</div>
<form className = 'login-flex-item' onSubmit = {goToLogin}>
<input className = 'form-item'
type = 'text'
name = 'name'
placeholder = "Enter Name"
value = {state.credentials.name}
onChange ={handleChange}
/>
<input className = 'form-item'
type = 'text'
name = 'username'
placeholder = "Username"
value = {state.credentials.username}
onChange ={handleChange}
/>
<input className = 'form-item'
type="password"
name="password"
placeholder = "Password"
value={state.credentials.password}
onChange={handleChange}
/>
<button className = 'form-item btn'>Sign Up</button>
</form>
</div>
</div>
</div>
);
};
export default Register;
这是测试失败的描述我得到任何帮助都很棒我已经盯着这个看了好几个小时但似乎找不到解决方案。
FAIL src/App.test.js
App component
√ renders page header and login component on default route (33 ms)
× Renders page header and Register component on Register route (198 ms)
● App component › Renders page header and Register component on Register route
Login(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
35 | Register.mockImplementation(()=> <div>RegisterMock</div>);
36 |
> 37 | render(
| ^
38 | <MemoryRouter initialEntries={['/register']}>
39 | <App />
40 | </MemoryRouter>
at reconcileChildFibers (node_modules/react-dom/cjs/react-dom.development.js:14169:23)
at reconcileChildren (node_modules/react-dom/cjs/react-dom.development.js:16990:28)
at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17890:5)
at beginWork (node_modules/react-dom/cjs/react-dom.development.js:19049:16)
at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:3945:14)
at HTMLUnknownElement.callTheUserObjectsOperation (node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:338:25)
at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:274:3)
at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:221:9)
at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:94:17)
at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:231:34)
at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:3994:16)
at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:4056:31)
at beginWork (node_modules/react-dom/cjs/react-dom.development.js:23964:7)
at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:22779:12)
at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:22707:5)
at renderRootSync (node_modules/react-dom/cjs/react-dom.development.js:22670:7)
at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:22293:18)
at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21881:7)
at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:25482:3)
at node_modules/react-dom/cjs/react-dom.development.js:26021:7
at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:22431:12)
at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:26020:5)
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:26103:10)
at node_modules/@testing-library/react/dist/pure.js:101:25
at batchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:22380:12)
at act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:1042:14)
at render (node_modules/@testing-library/react/dist/pure.js:97:26)
at Object.<anonymous> (src/App.test.js:37:7)
来自文档 <Router>
:
The common low-level interface for all router components. Typically apps will use one of the high-level routers instead:
<BrowserRouter>
<HashRouter>
<MemoryRouter>
<NativeRouter>
<StaticRouter>
问题是 <BrowserRouter>
组件像这样包装在 <MemoryRouter>
中:
<MemoryRouter>
<BrowserRouter>
<Switch>
....
</Switch>
</BrowserRouter>
</MemoryRouter>
这将为您提供来自 <BrowserRouter>
的 location
,它将是:
location: { pathname: '/', search: '', hash: '', state: undefined }
您应该将 <Switch>
及其子项拆分为一个独立的组件,例如 Routes
:
import React from 'react';
import { BrowserRouter as Router, Route, Switch, useLocation } from 'react-router-dom';
const Login = () => <div>login</div>;
const Register = () => <div>Register</div>;
export const Routes = () => {
const location = useLocation();
console.log('location: ', location);
return (
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/register" component={Register} />
</Switch>
);
};
function App() {
return (
<Router>
<Routes />
</Router>
);
}
export default App;
以便您可以使用 Routes
和 <MemoryRouter>
进行测试:
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { Routes } from './app';
describe('App component', () => {
it('renders page header and login component on default route', () => {
render(
<MemoryRouter>
<Routes />
</MemoryRouter>
);
expect(screen.getByText('login')).toBeInTheDocument();
});
it('Renders page header and Register component on Register route', () => {
render(
<MemoryRouter initialEntries={['/register']}>
<Routes />
</MemoryRouter>
);
expect(screen.getByText('Register')).toBeInTheDocument();
});
});
测试结果:
PASS examples/70090905/app.test.tsx (8.087 s)
App component
✓ renders page header and login component on default route (45 ms)
✓ Renders page header and Register component on Register route (3 ms)
console.log
location: {
pathname: '/',
search: '',
hash: '',
state: undefined,
key: 'zlnxcn'
}
at Routes (examples/70090905/app.tsx:9:11)
console.log
location: {
pathname: '/register',
search: '',
hash: '',
state: undefined,
key: '8z804n'
}
at Routes (examples/70090905/app.tsx:9:11)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.625 s, estimated 11 s
出于说明和演示目的,我删除了不必要的代码。
包版本:
"react-router-dom": "^5.2.0"