You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 12:48:34