You need to enable JavaScript to run this app. 如何用Jest、RTL测试Ionic React组件的输入状态更新与表单提交
优惠活动
大模型
产品
解决方案
定价
更多

如何用Jest、RTL测试Ionic React组件的输入状态更新与表单提交

注意:你的组件代码中IonInput的value属性硬编码为了"sss",需要修改为value={username}才能成为正常的受控组件,否则输入内容不会同步到视图,也会影响测试结果。

表单提交测试适配方案

1. 前置依赖Mock

提交逻辑依赖外部SDK服务和路由跳转能力,测试前需要先Mock这两个依赖,避免调用真实接口产生副作用:

// 测试文件顶部添加Mock逻辑
import { Service } from 'src/libs/sdk'
import { useHistory } from 'react-router-dom'

jest.mock('src/libs/sdk', () => ({
  Service: {
    loginControllerResetPassword: jest.fn()
  }
}))

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useHistory: jest.fn()
}))

// 每个测试用例执行前重置Mock状态
beforeEach(() => {
  jest.clearAllMocks()
  ;(useHistory as jest.Mock).mockReturnValue({ push: jest.fn() })
})

2. 完整提交逻辑测试用例

覆盖成功、失败两种核心场景,需要用到waitFor等待异步状态更新完成:

提交成功场景

const TEST_EMAIL = 'user@example.com'

test('提交成功时调用重置密码服务并显示模态框', async () => {
  // 模拟接口返回成功
  (Service.loginControllerResetPassword as jest.Mock).mockResolvedValue({ code: 200 })
  
  const { getByTestId, getByText } = render(<PasswordReset />)
  
  // 输入邮箱
  const input = getByTestId('input-field')
  fireEvent.ionInput(input, TEST_EMAIL)
  
  // 触发提交
  fireEvent.click(getByTestId('password-reset-button'))
  
  // 验证服务被正确调用
  expect(Service.loginControllerResetPassword).toHaveBeenCalledWith({ username: TEST_EMAIL })
  
  // 等待异步操作完成,验证模态框显示
  await waitFor(() => {
    expect(getByText('Ok')).toBeInTheDocument()
  })
})

提交失败场景

test('提交失败时显示错误提示', async () => {
  // 模拟接口返回错误
  const mockError = new Error('邮箱账号不存在')
  (Service.loginControllerResetPassword as jest.Mock).mockRejectedValue(mockError)
  
  const { getByTestId } = render(<PasswordReset />)
  
  fireEvent.ionInput(getByTestId('input-field'), 'wrong@example.com')
  fireEvent.click(getByTestId('password-reset-button'))
  
  // 验证错误信息渲染
  await waitFor(() => {
    expect(getByTestId('reset-error')).toHaveTextContent(mockError.message)
  })
})
状态更新逻辑覆盖方案

你需要覆盖的handleUsernameInput函数不需要单独测试内部状态,通过行为验证即可实现覆盖率达标:

test('输入内容时同步更新状态', () => {
  const { getByTestId } = render(<PasswordReset />)
  const input = getByTestId('input-field')
  const testValue = 'test@demo.com'
  
  fireEvent.ionInput(input, testValue)
  // 验证输入框值与输入内容一致,即可证明状态更新逻辑生效
  expect(input).toHaveValue(testValue)
})

如果要覆盖dismissModal函数,补充如下用例:

test('模态框确认后跳转至登录页', async () => {
  const mockPush = jest.fn()
  ;(useHistory as jest.Mock).mockReturnValue({ push: mockPush })
  ;(Service.loginControllerResetPassword as jest.Mock).mockResolvedValue({})
  
  const { getByTestId, getByText } = render(<PasswordReset />)
  fireEvent.ionInput(getByTestId('input-field'), TEST_EMAIL)
  fireEvent.click(getByTestId('password-reset-button'))
  
  await waitFor(() => getByText('Ok'))
  fireEvent.click(getByText('Ok'))
  
  expect(mockPush).toHaveBeenCalledWith('/login')
})
代码覆盖率统计配置

配置项

在项目根目录的jest.config.js或者package.json的jest配置段添加如下配置:

{
  "collectCoverageFrom": [
    "src/**/*.{ts,tsx}",
    "!src/**/*.d.ts",
    "!src/**/index.ts"
  ],
  "coverageReporters": ["text", "html", "lcov"],
  "coverageThreshold": {
    "global": {
      "statements": 90,
      "branches": 80,
      "functions": 90,
      "lines": 90
    }
  }
}

运行命令

执行测试时添加--coverage参数即可生成覆盖率报告:

npm run test -- --coverage

运行完成后会在控制台输出覆盖率统计,同时生成coverage目录,打开coverage/lcov-report/index.html可以查看每行代码的覆盖情况。

内容的提问来源于stack exchange,提问作者Ahmed J.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 04:06:05