移动端测试 mobile-testing

React Native移动应用测试技能,提供使用Jest和React Native Testing Library进行组件测试、Hook测试、工具函数测试的完整指南。包含测试设置、模板代码、模拟模式、最佳实践和故障排除方法,适用于移动开发工程师进行自动化测试开发。关键词:React Native测试,Jest测试,移动端自动化测试,组件测试,测试覆盖率

移动开发 0 次安装 0 次浏览 更新于 3/2/2026

name: mobile-testing description: 使用Jest和React Native Testing Library为React Native应用编写和运行测试。适用于创建测试、调试失败或设置测试基础设施时使用。 allowed-tools: Bash, Read, Write, Edit

移动端测试

React Native应用程序测试指南。

何时使用

  • 为组件或工具编写单元测试
  • 为功能创建集成测试
  • 设置测试基础设施
  • 调试测试失败
  • 提高测试覆盖率

测试设置

# 安装测试依赖
npm install --save-dev jest @testing-library/react-native

# 添加到package.json
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  }
}

组件测试模板

import { render, fireEvent } from '@testing-library/react-native';
import { MyButton } from './MyButton';

describe('MyButton', () => {
  it('正确渲染', () => {
    const { getByText } = render(<MyButton title="点击我" />);
    expect(getByText('点击我')).toBeTruthy();
  });

  it('按下时调用onPress', () => {
    const onPress = jest.fn();
    const { getByText } = render(
      <MyButton title="点击我" onPress={onPress} />
    );

    fireEvent.press(getByText('点击我'));
    expect(onPress).toHaveBeenCalledTimes(1);
  });
});

Hook测试模板

import { renderHook, act } from '@testing-library/react-native';
import { useCounter } from './useCounter';

describe('useCounter', () => {
  it('增加计数', () => {
    const { result } = renderHook(() => useCounter());

    act(() => {
      result.current.increment();
    });

    expect(result.current.count).toBe(1);
  });
});

工具函数测试模板

import { formatDate } from './formatDate';

describe('formatDate', () => {
  it('正确格式化日期', () => {
    const date = new Date('2025-01-15');
    expect(formatDate(date)).toBe('2025-01-15');
  });

  it('处理无效输入', () => {
    expect(() => formatDate(null)).toThrow();
  });
});

模拟模式

模拟外部模块

jest.mock('expo-notifications', () => ({
  scheduleNotificationAsync: jest.fn(),
}));

模拟数据库

jest.mock('@/db/client', () => ({
  db: {
    select: jest.fn(),
    insert: jest.fn(),
  },
}));

模拟导航

jest.mock('expo-router', () => ({
  useRouter: () => ({
    push: jest.fn(),
    back: jest.fn(),
  }),
}));

运行测试

# 运行所有测试
npm test

# 监听模式(变更时自动重新运行)
npm run test:watch

# 生成覆盖率报告
npm run test:coverage

# 运行特定测试文件
npm test -- MyComponent.test.tsx

# 更新快照
npm test -- -u

最佳实践

  1. 测试行为,而非实现:测试用户看到和操作的内容
  2. 使用testID进行选择:比文本匹配更可靠
  3. 模拟外部依赖:保持测试隔离和快速
  4. 测试边界情况:空状态、错误、加载状态
  5. 保持测试简单:尽可能每个测试只有一个断言
  6. 清理:使用beforeEach/afterEach进行设置/拆卸

常见模式

异步测试

it('加载数据', async () => {
  const { findByText } = render(<DataComponent />);
  expect(await findByText('已加载')).toBeTruthy();
});

测试表单

it('验证输入', () => {
  const { getByTestId, getByText } = render(<LoginForm />);

  fireEvent.changeText(getByTestId('email-input'), '无效');
  fireEvent.press(getByTestId('submit-button'));

  expect(getByText('邮箱无效')).toBeTruthy();
});

测试列表

it('渲染列表项', () => {
  const items = [{ id: '1', name: '项目1' }];
  const { getAllByTestId } = render(<ItemList items={items} />);

  expect(getAllByTestId('list-item')).toHaveLength(1);
});

故障排除

  • 测试超时:增加超时时间或检查未解决的Promise
  • 找不到元素:使用screen.debug()查看渲染输出
  • 模拟不工作:确保模拟在导入之前
  • 异步问题:使用waitForfindBy查询

资源