移动端测试的特殊挑战
移动端测试比 Web 开发更复杂,原因是多方面的:设备碎片化(Android 有数千种设备,iOS 有不同屏幕尺寸)、操作系统版本差异、权限管理(相机、位置、推送等需要用户授权)、网络状态变化、后台/前台切换行为等,都是 Web 开发几乎不需要面对的挑战。
因此移动端测试策略通常分层:单元测试覆盖业务逻辑(快速、稳定);集成测试覆盖组件交互;E2E 测试覆盖关键用户流程(慢但最真实)。
核心名词解释
Jest
Facebook 开发的 JavaScript 测试框架,React Native 内置集成。提供测试运行器、断言库、Mock 功能、快照测试。是 RN 单元测试和集成测试的标准选择。
@testing-library/react-native
组件测试库,提供 render(渲染组件)、screen(查询 DOM)、fireEvent(模拟用户操作)等 API。哲学:测试组件的行为而非实现细节。
Detox
Wix 开源的 React Native E2E 测试框架,"灰盒测试"方案——测试代码运行在 Node.js,但直接操控真实模拟器/设备上的原生 UI,比纯黑盒测试更稳定。
EAS Build
Expo 的云端构建服务。在 Expo 的服务器上运行 Xcode 和 Android SDK,生成 .ipa 和 .apk/.aab 文件,你无需本地安装这些工具。对 CI/CD 非常友好。
EAS Submit
Expo 的自动提交服务,配合 EAS Build 构建完成后自动上传到 App Store Connect 或 Google Play Console,省去手动上传步骤。
TestFlight
Apple 官方内部测试平台,上传 .ipa 后可邀请测试人员安装(无需上架 App Store)。内部测试最多 100 人,外部测试最多 10000 人。
OTA Update(Over The Air)
热更新,通过 expo-updates 将新的 JS Bundle 推送到用户设备,无需提交 App Store 审核。适合紧急 Bug 修复和小功能更新。注意:纯原生代码变更不能 OTA,必须走完整发布流程。
发布流程总览
React Native 应用发布流程
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
代码变更
│
▼
Jest 单元测试(PR 时自动运行)
│ 失败 → 修复
▼
Detox E2E 测试(关键流程验证)
│
▼
eas build --profile preview ← 内部测试构建
│
▼
TestFlight / Google 内部测试 ← 邀请测试人员
│ 发现 Bug → 修复
▼
eas build --profile production ← 生产构建
│
▼
eas submit ← 自动提交审核
│
├── App Store 审核:1-3 天
│
└── Google Play 审核:2-7 天
│
▼
正式上线 🚀
│
▼
小 Bug 修复 → expo-updates OTA ← 无需重新审核!
单元测试:Jest + Testing Library
// components/__tests__/LikeButton.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react-native';
import { LikeButton } from '../LikeButton';
// Mock React Query 的 useMutation
const mockMutate = jest.fn();
jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
useMutation: () => ({ mutate: mockMutate, isPending: false }),
}));
describe('LikeButton', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('显示初始点赞数', () => {
render(<LikeButton postId="p1" initialLikes={42} liked={false} />);
expect(screen.getByText('42')).toBeTruthy();
});
it('点击后调用 like API', async () => {
render(<LikeButton postId="p1" initialLikes={10} liked={false} />);
const button = screen.getByRole('button', { name: /点赞/ });
fireEvent.press(button);
await waitFor(() => {
expect(mockMutate).toHaveBeenCalledWith('p1');
});
});
it('乐观更新:点击后立即显示 +1', () => {
render(<LikeButton postId="p1" initialLikes={10} liked={false} />);
fireEvent.press(screen.getByRole('button'));
// 乐观更新,不等待网络,立即显示 11
expect(screen.getByText('11')).toBeTruthy();
});
it('快照测试', () => {
const { toJSON } = render(
<LikeButton postId="p1" initialLikes={5} liked={true} />
);
expect(toJSON()).toMatchSnapshot();
});
});
EAS Build 配置
// eas.json — EAS Build 配置文件
{
"cli": {
"version": ">= 7.0.0"
},
"build": {
"development": {
"developmentClient": true, // Development Build(支持自定义原生模块)
"distribution": "internal",
"ios": { "simulator": true } // 支持模拟器
},
"preview": {
"distribution": "internal", // 内部分发(TestFlight / 内部测试)
"channel": "preview"
},
"production": {
"distribution": "store", // 应用商店分发
"channel": "production",
"ios": {
"buildConfiguration": "Release"
},
"android": {
"buildType": "app-bundle" // AAB 格式(Google Play 要求)
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "your@apple.com",
"ascAppId": "1234567890"
},
"android": {
"serviceAccountKeyPath": "./google-service-account.json",
"track": "internal" // 先发内部测试轨道
}
}
}
}
OTA 热更新配置
// app.json — expo-updates 配置
{
"expo": {
"updates": {
"url": "https://u.expo.dev/your-project-id",
"checkAutomatically": "ON_LOAD" // 启动时检查更新
},
"runtimeVersion": {
"policy": "sdkVersion" // 根据 SDK 版本决定兼容性
}
}
}
// hooks/useOTA.ts — 手动检查并应用更新
import * as Updates from 'expo-updates';
export function useOTAUpdate() {
useEffect(() => {
async function checkUpdate() {
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
// 提示用户重启以应用更新
Alert.alert('发现新版本', '重启应用以应用更新', [
{ text: '稍后', style: 'cancel' },
{
text: '立即重启',
onPress: () => Updates.reloadAsync(),
},
]);
}
} catch (e) {
console.log('OTA 检查失败', e);
}
}
checkUpdate();
}, []);
}
审核周期提醒
App Store 通常需要 1-3 个工作日审核(有时更快),Google Play 首次上架通常需要 2-7 天。版本发布要提前计划,不能临时抱佛脚。重大发布建议在节假日前至少两周提交审核,避免假期期间审核队伍减员导致延误。
灰度发布
App Store 支持分阶段发布(Phased Release),可以先向 1% / 2% / 5% / 10% / 20% / 50% / 100% 的用户逐步推送。Google Play 支持按百分比的正式发布轨道。利用灰度发布可以在大规模推广前发现崩溃和用户反馈问题。