【问题标题】:Failing Test: Nothing was returned from render. identical test passes, only difference with the failing test is the use of initialEntries测试失败:渲染没有返回任何内容。相同的测试通过,与失败测试的唯一区别是使用 initialEntries
【发布时间】:2022-01-02 12:53:43
【问题描述】:

我正在尝试测试我的路由是否正常工作,当我测试默认“/”路由时第一个测试通过,但是第二个测试没有通过。唯一的区别是在第二次测试中引入了initialEntries={['/register']},因此第二次测试失败:

"Login(...): 没有从渲染返回。这通常意味着 缺少返回语句。或者,不渲染任何内容,返回 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? &nbsp;
                        <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$1 (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$1 (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)

【问题讨论】:

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


    【解决方案1】:

    来自文档&lt;Router&gt;

    所有路由器组件的通用低级接口。通常,应用会使用其中一个高级路由器:

    • &lt;BrowserRouter&gt;
    • &lt;HashRouter&gt;
    • &lt;MemoryRouter&gt;
    • &lt;NativeRouter&gt;
    • &lt;StaticRouter&gt;

    问题是&lt;BrowserRouter&gt; 组件被包裹在&lt;MemoryRouter&gt; 中,如下所示:

    <MemoryRouter>
      <BrowserRouter>
        <Switch>
         ....
        </Switch>
      </BrowserRouter>
    </MemoryRouter>
    

    这将为您提供来自&lt;BrowserRouter&gt;location,它将是:

    location:  { pathname: '/', search: '', hash: '', state: undefined }
    

    您应该将&lt;Switch&gt; 及其子组件拆分为一个独立的组件,例如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&lt;MemoryRouter&gt; 进行测试:

    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"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-31
      • 1970-01-01
      • 1970-01-01
      • 2015-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-25
      相关资源
      最近更新 更多