使用MemoryRouter测试带参数Profile组件失败:无法找到heading角色
问题:Profile组件单元测试无法找到heading角色元素
我正在对Profile组件进行单元测试,该组件通过useParams()从路径变量中获取userId,展示用户个人资料信息。测试用MemoryRouter传入了正确参数,但始终提示无法找到role为"heading"的元素,可组件JSX里明明有2个默认带heading角色的标题元素,不确定是测试配置问题还是其他原因。
报错信息
Unable to find role="heading" Ignored nodes: comments, <script />, <style /> <body> <div /> </body> Ignored nodes: comments, <script />, <style /> <html> <head /> <body> <div /> </body> </html> Error: Unable to find role="heading" Ignored nodes: comments, <script />, <style /> <body> <div /> </body>
测试代码(Profile.test.js)
import React from 'react' // import API mocking utilities from Mock Service Worker import {rest} from 'msw' import {setupServer} from 'msw/node' // import react-testing methods import {render, fireEvent, waitFor, screen} from '@testing-library/react' import {MemoryRouter, Route, Routes} from 'react-router'; // add custom jest matchers from jest-dom import '@testing-library/jest-dom'; import config from "../configuration/Configuration.json"; import {Profile} from "../pages/Profile"; const userId = 1; const server = setupServer( rest.get( config.URL + '/users/' + userId, (req, res, ctx) => { return res(ctx.json({ userId: 1, email: 'testEmail@outlook.com', password: null, firstName: 'Test', lastName: 'User' })) } ), ) const renderWithRouter = ({ children }) => ( render( <MemoryRouter initialEntries={['/users/1']}> <Routes> <Route path='/users/:userId'> {children} </Route> </Routes> </MemoryRouter> ) ) beforeAll(() => server.listen()) afterEach(() => server.resetHandlers()) afterAll(() => server.close()) test('loads and displays profile', async () => { renderWithRouter(<Profile />) await waitFor( () => screen.getByRole("heading").toHaveTextContent("Test User") ) })
组件代码(Profile.js)
import React from 'react' import {useEffect, useState} from "react"; import UserService from "../services/UserService"; import {getFullName} from "../util/Util"; import '../css/profile.css'; import {useParams} from "react-router-dom"; export const Profile = () => { //array of compatible users fetched for a user. const [userProfileInformation, setUserProfileInformation] = useState([]); const [isLoading, setLoading] = useState(true); const { userId } = useParams(); useEffect(() => { getUserProfileInformation().then(() => { setLoading(false); }); }, []); const getUserProfileInformation = async () => { const response = await UserService.getUserProfileInformation(userId) .then(response => response.json()) .then(data => { setUserProfileInformation(data); }); } if (isLoading) { return ( <div id="loading"> <h2>Loading...</h2> </div> ) } return ( <div> <div className="profileCard"> <h1 name='fullName'> {getFullName( userProfileInformation.firstName, userProfileInformation.lastName )} </h1> <h2>{userProfileInformation.email}</h2> </div> </div> ) }
修复方案
1. 修正React Router v6的路由配置(核心问题)
React Router v6中,Route组件不再通过children属性渲染内容,必须使用element属性传入要渲染的组件。你的测试路由配置没有正确渲染Profile组件,导致页面只有空div,自然找不到heading元素。
修改renderWithRouter中的Route部分:
const renderWithRouter = ({ children }) => ( render( <MemoryRouter initialEntries={['/users/1']}> <Routes> {/* 把children放到element属性中 */} <Route path='/users/:userId' element={children} /> </Routes> </MemoryRouter> ) )
2. 修正组件初始状态类型
Profile组件中userProfileInformation初始设为数组[],但接口返回的是单个用户对象,这会导致初始渲染时getFullName取不到属性值。改为对象初始值:
const [userProfileInformation, setUserProfileInformation] = useState({});
3. 优化测试写法(符合RTL最佳实践)
用findByRole替代waitFor + getByRole,findByRole本身就是异步查询方法,更简洁:
test('loads and displays profile', async () => { renderWithRouter(<Profile />) const heading = await screen.findByRole("heading", { name: /test user/i }) expect(heading).toHaveTextContent("Test User") })
4. 统一异步代码写法(可选)
组件中getUserProfileInformation混用了await和then,建议统一用async/await风格,避免嵌套:
const getUserProfileInformation = async () => { try { const response = await UserService.getUserProfileInformation(userId); const data = await response.json(); setUserProfileInformation(data); } catch (error) { console.error('获取用户资料失败:', error); // 可添加错误状态处理,比如提示用户 } }
内容的提问来源于stack exchange,提问作者devo9191
相关产品推荐
相关产品推荐

