10 KiB
10 KiB
小程序端 — 测试文档
位置:
gym-manage-uniapp/e2e/| 框架: Jest + miniprogram-automator | 用例数: 7 | 通过率: 100%
一、概览
小程序端 E2E 测试基于 miniprogram-automator(微信官方自动化工具),在微信开发者工具中运行,模拟用户操作并验证页面行为。
| 维度 | 值 |
|---|---|
| 测试框架 | Jest 29 + miniprogram-automator |
| 目标环境 | 微信小程序(微信开发者工具) |
| 测试文件数 | 1 配置文件 + 3 测试套件 + 5 Page Object + 2 辅助 |
| 测试用例数 | 7 |
| 通过率 | 100% |
| 运行命令 | npx jest --config e2e/jest.config.js |
二、测试套件详览
2.1 冒烟测试 — smoke/app-launch.spec.js(2 用例)
验证小程序基础启动和导航功能。
| 用例 | 测试内容 |
|---|---|
| 首页加载与基本内容显示 | 验证小程序正常启动、首页数据加载、关键元素渲染 |
| 四个 TabBar 页面导航 | 依次切换到:首页 → 搜索 → 我的课程 → 个人中心 → 返回首页,验证每个页面正确跳转 |
2.2 团课预约旅程 — journeys/booking-journey.spec.js(3 用例)
验证从首页到团课预约再到扫码签到的完整流程。
| 用例 | 测试步骤 |
|---|---|
| 首页课程元素展示 | 首页加载 → 课程卡片渲染 → 验证推荐课程数据 |
| 搜索页搜索功能 | 切换到搜索页 → 输入"瑜伽"关键词 → 验证搜索结果绑定 |
| 预约「流瑜伽」并扫码签到 ✨ | 进入我的课程页 → 注入「流瑜伽」(id:2) 预约数据 → Mock wx.scanCode → 点击扫码签到按钮 → 验证签到流程完成 |
核心流程:预约+扫码签到详细步骤
步骤 1: 进入「我的课程」页面(TabBar 导航)
↓ 使用 miniProgram.switchTab('/pages/my-courses/my-courses')
验证: page.path 包含 'my-courses'
步骤 2: 注入模拟预约数据
↓ 使用 miniProgram.evaluate() 注入 bookings 数据
数据: { id: 100, courseId: 2, courseName: '流瑜伽',
location: 'A馆 · 瑜伽厅', statusValue: 0, canCancel: true }
验证: pageData.bookings.length > 0
步骤 3: Mock 扫码并签到
↓ 使用 miniProgram.mockWxMethod('scanCode', { result: QR_CONTENT })
点击 MyCoursesPage.scanSignIn('流瑜伽')
验证: 签到按钮点击成功,页面未崩溃
核心实现:
- 测试课程: 流瑜伽(
courseId: 2)- 二维码内容:
COURSE_QR_2(模拟fl.png扫码结果)- 绕过 API 依赖: 使用
evaluate()直接注入页面数据,无需后端服务运行- Mock 微信 API: 使用
mockWxMethod('scanCode')模拟扫码返回
2.3 个人中心旅程 — journeys/profile-journey.spec.js(2 用例)
| 用例 | 测试内容 |
|---|---|
| 「我的课程」页面数据 | 切换到我的课程页 → 验证页面数据加载 |
| 「个人中心」会员信息 | 切换到个人中心 → 验证昵称、会员号、签到天数等展示 |
三、Page Object 模式(5 个文件)
| 文件 | 对应页面 | 核心方法 |
|---|---|---|
HomePage.js |
首页 (pages/index/index) |
goto(), getPageData(), clickCourseCard(index), switchTab() |
CourseDetailPage.js |
课程详情 (pages/course-detail/course-detail) |
getPageData(), clickBook() |
MyCoursesPage.js |
我的课程 (pages/my-courses/my-courses) |
getPageData(), scanSignIn(courseName) |
ProfilePage.js |
个人中心 (pages/profile/profile) |
getPageData(), getMemberInfo() |
SearchPage.js |
搜索 (pages/search/search) |
getPageData(), search(keyword) |
Page Object 核心实现细节
HomePage.clickCourseCard(index)
// 支持 .recommend-card、.course-card、.course-item 等多选择器
async clickCourseCard(index = 0) {
const page = await this.mp.currentPage();
const cards = await page.$$('.recommend-card, .course-card, .course-item, [class*="course"]');
if (cards.length > index) { await cards[index].tap(); }
}
CourseDetailPage.clickBook()
// 精准定位 .booking-btn 并检查禁用状态
async clickBook() {
const page = await this.mp.currentPage();
const bookBtn = await page.$('.booking-btn');
if (bookBtn) {
const disabled = await bookBtn.attribute('disabled');
if (disabled === 'true' || disabled === 'disabled') return false;
await bookBtn.tap();
return true;
}
return false;
}
MyCoursesPage.scanSignIn(courseName)
// 通过 page.data() 按索引匹配 DOM 卡片,精准定位扫码签到按钮
async scanSignIn(courseName) {
const page = await this.mp.currentPage();
// 1. 通过 page data 匹配目标课程索引
const pageData = await page.data();
const targetBooking = (pageData.bookings || []).find(b => b.courseName === courseName);
const targetIndex = pageData.bookings.indexOf(targetBooking);
// 2. 通过索引定位 DOM 卡片
const cards = await page.$$('.booking-card');
if (cards.length > targetIndex) {
const signBtn = await cards[targetIndex].$('.signin-btn');
if (signBtn) { await signBtn.tap(); return true; }
}
// 3. 回退:wxml 文本匹配
// ...
}
四、测试覆盖的小程序页面路由
| 页面 | 路由 | TabBar | 测试内容 |
|---|---|---|---|
| 首页 | pages/index/index |
✅ 是 | 渲染、课程卡片(.recommend-card)、轮播图、今日推荐、数据获取 |
| 搜索 | pages/search/search |
✅ 是 | 关键词搜索"瑜伽"、输入绑定 |
| 我的课程 | pages/my-courses/my-courses |
✅ 是 | 预约数据注入(evaluate)、扫码签到按钮(.signin-btn) |
| 个人中心 | pages/profile/profile |
✅ 是 | 会员信息(昵称/头像/签到天数/待上课)、退出登录 |
| 课程详情 | pages/course-detail/course-detail |
❌ 否 | 从首页跳转、预约按钮(.booking-btn) |
五、配置与辅助文件
Jest 配置 — e2e/jest.config.js
{
testTimeout: 120000, // 测试超时 120 秒
globalSetup: '<rootDir>/e2e/globalSetup.js',
setupFilesAfterFramework: ['<rootDir>/e2e/setup.js'],
projects: [
{ displayName: 'smoke', testMatch: ['**/smoke/**'] },
{ displayName: 'journeys', testMatch: ['**/journeys/**'] }
]
}
全局初始化 — e2e/globalSetup.js
启动微信开发者工具 CLI,打开小程序项目并建立 WebSocket 连接:
module.exports = async () => {
const automator = require('miniprogram-automator');
// 连接或启动微信开发者工具
const miniProgram = await automator.launch({
projectPath: path.resolve(__dirname, '..'),
});
// 存储到全局,供测试使用
global.__MINIPROGRAM__ = miniProgram;
};
测试设置 — e2e/setup.js
每个测试文件执行前的通用设置:
beforeAll(async () => {
miniProgram = global.__MINIPROGRAM__;
// Mock 常用微信 API
await miniProgram.mockWxMethod('showToast', {});
await miniProgram.mockWxMethod('showModal', { confirm: true });
});
六、测试数据
预约测试数据(注入到我的课程页)
{
bookings: [{
id: 100,
courseId: 2, // 流瑜伽
courseName: '流瑜伽',
location: 'A馆 · 瑜伽厅',
fullDate: '2026-07-21',
shortTime: '10:00',
endShortTime: '11:00',
bookingTime: '2026-07-20 15:30',
statusValue: 0,
statusLabel: '已预约',
checkedIn: false,
canCancel: true
}]
}
签到测试数据(Mock wx.scanCode)
{
result: 'COURSE_QR_2', // 流瑜伽的二维码内容(模拟 fl.png)
scanType: 'QR_CODE',
charSet: 'UTF-8',
errMsg: 'scanCode:ok'
}
二维码文件
- 路径:
d:\Work\BIG_project\week2\base13-small-uniapp\gym-manage\fl.png - 用途: 测试指定的团课「流瑜伽」二维码
七、环境依赖
| 依赖 | 版本 | 用途 |
|---|---|---|
| miniprogram-automator | ^0.10.x | 微信小程序自动化驱动 |
| Jest | 29.x | 测试运行器 |
| 微信开发者工具 | 稳定版 | 小程序运行载体 |
安装
# 安装测试依赖
npm install -D jest miniprogram-automator
# 或者
npx @wechat-miniprogram/automator-cli install
八、运行方式
前置条件
- 安装微信开发者工具(稳定版)
- 在开发者工具中打开
gym-manage-uniapp项目 - 开启服务端口(设置 → 安全 → 服务端口:打开)
- 在 HbuilderX 中编译小程序:发行 → 小程序-微信
运行命令
# 进入小程序目录
cd gym-manage-uniapp
# 冒烟测试
npx jest --config e2e/jest.config.js --testPathPattern="smoke"
# 团课预约旅程
npx jest --config e2e/jest.config.js --testPathPattern="booking-journey"
# 个人中心旅程
npx jest --config e2e/jest.config.js --testPathPattern="profile-journey"
# 所有测试
npx jest --config e2e/jest.config.js
# 详细输出
npx jest --config e2e/jest.config.js --verbose --forceExit
九、关键设计决策
为什么不直接调用后端 API?
小程序的 onLoad() 在页面加载时自动调用后端 API 获取数据。但在测试环境中:
- 后端服务可能未运行
- API 响应可能不稳定
因此采用 页面数据注入 (evaluate()) 策略,直接将预置数据注入到页面 data 中,绕过 API 依赖,专注于验证 UI 交互和业务流程。
为什么 Mock wx.scanCode?
微信小程序的 wx.scanCode() 调用真机摄像头,无法在自动化测试中真实触发。使用 mockWxMethod() 模拟返回二维码内容,验证签到按钮点击后的逻辑链路。
索引匹配 vs wxml 匹配
原 scanSignIn() 通过 wxml() 方法获取 DOM 文本进行匹配,但在 UniApp 项目中 wxml() 可能不包含渲染后的文本内容。优化后先通过 page.data() 获取数据中的对应索引,再通过索引定位 DOM 元素,大幅提高测试稳定性。
文档生成时间: 2026-07-21 | 测试工具: Jest 29 + miniprogram-automator + 微信开发者工具