react-native-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Native Testing Skill
React Native 测试技能
Learn comprehensive testing strategies including unit tests, component tests, and E2E tests.
学习全面的测试策略,包括单元测试、组件测试和端到端测试。
Prerequisites
前置要求
- React Native basics
- Understanding of async/await
- Familiarity with testing concepts
- React Native基础知识
- 理解async/await
- 熟悉测试概念
Learning Objectives
学习目标
After completing this skill, you will be able to:
- Configure Jest for React Native
- Write component tests with Testing Library
- Mock native modules and APIs
- Implement E2E tests with Detox/Maestro
- Set up CI/CD test pipelines
完成本技能学习后,你将能够:
- 为React Native配置Jest
- 使用Testing Library编写组件测试
- 模拟原生模块与API
- 使用Detox/Maestro实现端到端测试
- 搭建CI/CD测试流水线
Topics Covered
涵盖主题
1. Jest Setup
1. Jest 配置
javascript
// jest.config.js
module.exports = {
preset: 'react-native',
setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
transformIgnorePatterns: [
'node_modules/(?!(react-native|@react-native)/)',
],
};javascript
// jest.config.js
module.exports = {
preset: 'react-native',
setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
transformIgnorePatterns: [
'node_modules/(?!(react-native|@react-native)/)',
],
};2. Component Testing
2. 组件测试
tsx
import { render, screen, fireEvent } from '@testing-library/react-native';
import { Button } from './Button';
describe('Button', () => {
it('calls onPress when pressed', () => {
const onPress = jest.fn();
render(<Button title="Click" onPress={onPress} />);
fireEvent.press(screen.getByText('Click'));
expect(onPress).toHaveBeenCalled();
});
});tsx
import { render, screen, fireEvent } from '@testing-library/react-native';
import { Button } from './Button';
describe('Button', () => {
it('calls onPress when pressed', () => {
const onPress = jest.fn();
render(<Button title="Click" onPress={onPress} />);
fireEvent.press(screen.getByText('Click'));
expect(onPress).toHaveBeenCalled();
});
});3. Hook Testing
3. Hook测试
tsx
import { renderHook, act } from '@testing-library/react-native';
import { useCounter } from './useCounter';
describe('useCounter', () => {
it('increments count', () => {
const { result } = renderHook(() => useCounter());
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});
});tsx
import { renderHook, act } from '@testing-library/react-native';
import { useCounter } from './useCounter';
describe('useCounter', () => {
it('increments count', () => {
const { result } = renderHook(() => useCounter());
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});
});4. Mocking
4. 模拟
typescript
// Mock native module
jest.mock('@react-native-async-storage/async-storage', () => ({
setItem: jest.fn(),
getItem: jest.fn(),
}));
// Mock API
jest.mock('./api', () => ({
fetchUser: jest.fn().mockResolvedValue({ id: '1', name: 'Test' }),
}));typescript
// Mock native module
jest.mock('@react-native-async-storage/async-storage', () => ({
setItem: jest.fn(),
getItem: jest.fn(),
}));
// Mock API
jest.mock('./api', () => ({
fetchUser: jest.fn().mockResolvedValue({ id: '1', name: 'Test' }),
}));5. E2E with Detox
5. 使用Detox进行端到端测试
typescript
describe('Login', () => {
beforeAll(async () => {
await device.launchApp();
});
it('should login successfully', async () => {
await element(by.id('email')).typeText('test@example.com');
await element(by.id('password')).typeText('password');
await element(by.id('login-btn')).tap();
await expect(element(by.id('home'))).toBeVisible();
});
});typescript
describe('Login', () => {
beforeAll(async () => {
await device.launchApp();
});
it('should login successfully', async () => {
await element(by.id('email')).typeText('test@example.com');
await element(by.id('password')).typeText('password');
await element(by.id('login-btn')).tap();
await expect(element(by.id('home'))).toBeVisible();
});
});Quick Start Example
快速入门示例
tsx
// ProductCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react-native';
import { ProductCard } from './ProductCard';
const mockProduct = {
id: '1',
title: 'Test Product',
price: 99.99,
};
describe('ProductCard', () => {
it('renders product info', () => {
render(<ProductCard {...mockProduct} onPress={jest.fn()} />);
expect(screen.getByText('Test Product')).toBeOnTheScreen();
expect(screen.getByText('$99.99')).toBeOnTheScreen();
});
it('handles press', () => {
const onPress = jest.fn();
render(<ProductCard {...mockProduct} onPress={onPress} />);
fireEvent.press(screen.getByRole('button'));
expect(onPress).toHaveBeenCalledWith('1');
});
});tsx
// ProductCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react-native';
import { ProductCard } from './ProductCard';
const mockProduct = {
id: '1',
title: 'Test Product',
price: 99.99,
};
describe('ProductCard', () => {
it('renders product info', () => {
render(<ProductCard {...mockProduct} onPress={jest.fn()} />);
expect(screen.getByText('Test Product')).toBeOnTheScreen();
expect(screen.getByText('$99.99')).toBeOnTheScreen();
});
it('handles press', () => {
const onPress = jest.fn();
render(<ProductCard {...mockProduct} onPress={onPress} />);
fireEvent.press(screen.getByRole('button'));
expect(onPress).toHaveBeenCalledWith('1');
});
});Common Errors & Solutions
常见错误与解决方案
| Error | Cause | Solution |
|---|---|---|
| "Cannot find module" | Missing mock | Add jest.mock() |
| Async timeout | Missing await | Use waitFor() |
| Element not found | Wrong query | Check testID/role |
| 错误 | 原因 | 解决方案 |
|---|---|---|
| "Cannot find module" | 缺少模拟 | 添加jest.mock() |
| 异步超时 | 缺少await | 使用waitFor() |
| 元素未找到 | 查询错误 | 检查testID/角色 |
Validation Checklist
验证清单
- Unit tests pass
- Component tests cover interactions
- E2E tests complete flows
- Coverage meets threshold (80%+)
- 单元测试通过
- 组件测试覆盖交互逻辑
- 端到端测试覆盖完整流程
- 测试覆盖率达到阈值(80%+)
Usage
使用方法
Skill("react-native-testing")Bonded Agent:
06-react-native-testingSkill("react-native-testing")绑定Agent:
06-react-native-testing