React单元测试中Material UI Dialog组件报TypeError读取undefined错误
问题描述
开发基于Material UI的React应用时,已实现自定义Dialog组件MyCustomDialog并在主组件TokensTabViewContent中引入使用。使用React Testing Library对主组件MyMainComp执行单元测试时,渲染组件后触发错误:
Error: Uncaught [TypeError: Cannot read properties of undefined (reading 'MuiDialog-root')]
错误原因分析
- 组件内部变量冲突:
MyCustomDialog组件在解构props时已获取open和setOpen,但又重新声明了同名的useState变量,覆盖了传入的props值,导致组件内部状态逻辑混乱,同时引发作用域冲突问题。 - 缺少Material UI主题上下文:Material UI组件依赖
ThemeProvider提供的主题上下文生成样式类名(如MuiDialog-root)。测试代码直接渲染MyMainComp,未用ThemeProvider包裹,导致MUI组件无法获取主题对象,进而无法读取样式类名相关属性,触发undefined错误。
解决思路
1. 修复组件内部变量冲突
移除MyCustomDialog内部重复声明的useState,直接使用props传入的状态和状态更新函数:
import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Button } from '@mui/material'; import React from 'react'; type Props = { open: boolean; setOpen: React.Dispatch<React.SetStateAction<boolean>>; myAsyncTask: () => Promise<{ readonly status: string }>; }; export const MyCustomDialog: React.FC<Props> = ({ open, setOpen, myAsyncTask }) => { return <Dialog aria-labelledby='My Dialog' open={open} onClose={() => setOpen(false)}> <DialogTitle>My dialog</DialogTitle> <DialogContent> <DialogContentText>{`Do you agree on the terms?`}</DialogContentText> </DialogContent> <DialogActions> <Button onClick={() => setOpen(false)}>No</Button> <Button onClick={async () => { await myAsyncTask(); setOpen(false); }}>Yes</Button> </DialogActions> </Dialog>; }
2. 为测试添加ThemeProvider包裹
在测试代码中,用Material UI的ThemeProvider配合默认主题包裹待渲染组件,确保MUI组件能获取主题上下文:
import { render, screen } from '@testing-library/react'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import MyMainComp from './MyMainComp'; const defaultTheme = createTheme(); it('should render', async () => { render( <ThemeProvider theme={defaultTheme}> <MyMainComp /> </ThemeProvider> ); const userIdTxt = screen.getByText('user id'); expect(userIdTxt).toBeInTheDocument(); });
3. 可选:隔离Dialog组件测试
如果测试目标仅为主组件的其他部分,无需渲染Dialog,可以通过jest.mock模拟MyCustomDialog组件,避免渲染真实的MUI Dialog:
import { render, screen } from '@testing-library/react'; import MyMainComp from './MyMainComp'; // 模拟自定义Dialog组件,不渲染任何内容 jest.mock('./MyCustomDialog', () => ({ MyCustomDialog: () => null })); it('should render', async () => { render(<MyMainComp />); const userIdTxt = screen.getByText('user id'); expect(userIdTxt).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者user842225
相关产品推荐
相关产品推荐

