使用React Testing Library测试函数组件遇TypeError求解
问题描述
我有一个用于渲染消息的React函数组件MessageView,组件通过current函数根据传入的title和id获取对应消息。用React Testing Library(RTL)测试时,执行render(<MessageView title="sent" id={0} />)会触发TypeError:Cannot read properties of undefined (reading 'text'),错误出现在{msg!.text}这一行。我知道类组件可以监视属性/方法,但函数组件做不到,结合RTL的指导原则,想请教怎么定义msg.text或者用其他方式完成组件测试。
相关代码
测试代码
it('MailView', async () => { render(<MessageView title="sent" id={0} />) })
组件代码
export function MessageView({ title, id }: MessageViewProps) { const bucket = title === "inbox" ? received(user) : sent(user) const msg = current(bucket, id) // 获取当前邮件消息 return( <Box> {msg!.text} <------ 触发TypeError的位置 </Box> ); }
依赖版本
"@testing-library/jest-dom": "^5.16.5", "@testing-library/user-event": "^14.4.3", "@types/jest": "^27.5.2", "@types/node": "^16.18.36", "@types/react": "^18.2.13", "@types/react-dom": "^18.2.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "typescript": "^4.9.5",
相关函数与接口
export function current(items: Message[], idx: number) { return items[idx]; }
export interface Message { id: number date: string sender: string subject: string recipient: string text: string }
解决方案
问题核心是测试时current(bucket, id)返回了undefined,强行访问msg!.text导致报错。结合RTL"测试用户行为而非底层实现"的原则,提供以下几种可行方案:
1. 模拟received和sent函数的返回值
组件依赖received(user)和sent(user)获取消息列表,测试时用Jest模拟这两个函数,让它们返回包含指定id消息的数组:
// 在测试文件顶部模拟目标函数 jest.mock('./path-to-your-functions-file', () => ({ received: jest.fn(() => [{ id: 0, text: '测试收件箱消息', date: '2024-01-01', sender: 'test@example.com', subject: '测试主题', recipient: 'user@example.com' }]), sent: jest.fn(() => [{ id: 0, text: '测试已发送消息', date: '2024-01-01', sender: 'user@example.com', subject: '测试主题', recipient: 'test@example.com' }]), })); it('MailView 能正确渲染已发送消息', async () => { render(<MessageView title="sent" id={0} />); // 验证消息文本是否成功渲染 expect(screen.getByText('测试已发送消息')).toBeInTheDocument(); });
2. 给组件添加容错处理(推荐,同时提升生产环境稳定性)
在组件里增加空值检查,避免找不到消息时崩溃,也让测试场景更完整:
export function MessageView({ title, id }: MessageViewProps) { const bucket = title === "inbox" ? received(user) : sent(user) const msg = current(bucket, id) // 处理找不到消息的边界情况 if (!msg) { return <Box>暂无消息</Box>; } return( <Box> {msg.text} </Box> ); }
测试时可以分别验证两种场景:
// 测试无对应消息的情况 it('MailView 无对应消息时显示提示文本', async () => { jest.mock('./path-to-your-functions-file', () => ({ sent: jest.fn(() => []), })); render(<MessageView title="sent" id={0} />); expect(screen.getByText('暂无消息')).toBeInTheDocument(); });
3. 重构组件,通过props传入消息列表(解耦依赖)
把bucket的获取逻辑移到组件外部,通过props传入消息列表,测试时直接传入预设的测试数据即可:
// 重构后的组件 export function MessageView({ messages, id }: { messages: Message[], id: number }) { const msg = current(messages, id) if (!msg) { return <Box>暂无消息</Box>; } return( <Box> {msg.text} </Box> ); }
测试代码:
it('MailView 能渲染指定的测试消息', async () => { const testMessages = [{ id: 0, text: '测试消息', date: '2024-01-01', sender: 'test@example.com', subject: '测试主题', recipient: 'user@example.com' }]; render(<MessageView messages={testMessages} id={0} />); expect(screen.getByText('测试消息')).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者chocalaca
相关产品推荐
相关产品推荐

