从用户旅程到 E2E 测试 — 调研报告 | 2026-02-06
成熟项目在 UX 与测试之间有一条清晰的传导链。不是简单的"画 Storyboard → 写测试",而是分了 4 个递进层次:
用户旅程地图是最高层级的产品文档,描述用户从哪来、想做什么、过程中的情感变化和痛点。它不是技术文档,是产品/UX 文档。
| 元素 | 说明 | 示例 |
|---|---|---|
| 触发点 | 用户为什么打开 App | 收到朋友分享的预约链接 |
| 阶段 | 用户经历的主要步骤 | 浏览 → 选择 → 确认 → 完成 |
| 情感曲线 | 每个阶段的用户感受 | 好奇 → 犹豫(价格?) → 满足 |
| 触点 | 用户交互的 UI 元素 | 服务卡片、技师头像、时间选择器 |
| 痛点 | 可能导致放弃的障碍 | 需要注册吗?技师怎么选? |
| 机会点 | 可以优化的地方 | 推荐技师、显示评价 |
Google 提出的 CUJ(关键用户旅程)方法论强调:不是所有旅程都同等重要。应该按业务价值和使用频率排优先级,先覆盖最关键的路径。
| 阶段 | 用户行为 | 情感 | 触点 | 痛点 |
|---|---|---|---|---|
| 进入 | 点击朋友分享的链接 | 好奇 | 深度链接 | 加载慢? |
| 浏览 | 查看门店和服务列表 | 探索 | 服务卡片、价格 | 服务太多不知道选哪个 |
| 选择 | 选择服务和技师 | 犹豫 → 决定 | 技师卡片、任意技师 | 不了解技师水平 |
| 时间 | 选择预约时间 | 确认 | 时间选择器 | 想要的时间没有了 |
| 身份 | 输入手机号 | 轻微抗拒 | 手机号输入框 | 为什么要手机号? |
| 完成 | 确认预约 | 满足 | 成功页面 | 怎么取消/修改? |
User Flow 是技术化的动线图,描述每个页面之间的跳转逻辑和分支条件。与 Journey Map 的区别是:Journey Map 关注"情感",User Flow 关注"路径"。
BDD(行为驱动开发)是连接 UX 与测试的桥梁。用 Given-When-Then 格式把 User Flow 翻译成业务人员也能读懂的场景描述。
Scenario: 新用户通过分享链接完成单服务预约
Given 用户通过深度链接 "/booking/spa001" 打开 App
And 用户未登录
When 用户在服务列表选择 "经典美甲"
And 用户选择 "任意技师"
And 用户选择明天 14:00 的时段
And 用户输入手机号 "1234567890"
And 用户点击 "确认预约"
Then 显示预约成功页面
And 预约出现在 "我的预约" 列表中
Scenario: 已登录用户快速预约(跳过手机号)
Given 用户已通过邮箱登录
And 用户在门店 "spa001" 的首页
When 用户选择 "手部护理" 服务
And 用户选择技师 "Alice"
And 用户选择后天 10:00 的时段
And 用户确认预约
Then 直接显示预约成功(无需输入手机号)
Scenario: 用户添加多个服务到购物车并结账
Given 用户已登录
When 用户将 "经典美甲" 加入购物车
And 用户将 "足部护理" 加入购物车
Then 购物车显示 2 个服务
When 用户为每个服务选择技师和时间
And 用户点击结账
Then 显示结账成功页面
And "我的预约" 中显示 2 条预约记录
Scenario: 购物车中移除服务
Given 购物车中有 "经典美甲" 和 "足部护理"
When 用户滑动删除 "足部护理"
Then 购物车仅显示 "经典美甲"
And 总价更新为 "经典美甲" 单价
Scenario: 新用户邮箱注册
Given 用户在登录页面
When 用户点击 "注册"
And 用户填写邮箱 "test@example.com" 和密码
And 用户提交注册表单
Then 显示 "验证邮件已发送" 提示
Scenario: 手机号快捷登录(预约流程中)
Given 用户在预约确认页
And 用户未登录
When 系统跳转到手机号输入页
And 用户输入手机号并收到验证码
And 用户输入正确的验证码
Then 自动完成预约并跳转成功页
Scenario: 忘记密码重置流程
Given 用户在登录页
When 用户点击 "忘记密码"
And 用户输入注册邮箱
Then 显示 "重置链接已发送" 提示
BDD 场景指导测试代码的编写,但不是直接的代码生成。在 Flutter 中使用 integration_test 框架。
// integration_test/booking_single_service_test.dart
// 对应 BDD: 新用户通过分享链接完成单服务预约
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('新用户完成单服务预约流程', (tester) async {
// Given — 通过深度链接打开 App
app.main();
await tester.pump(Duration(seconds: 3));
// When — 选择服务
final serviceCard = find.text('经典美甲');
await tester.tap(serviceCard);
await tester.pump(Duration(seconds: 1));
// When — 选择 "任意技师"
await tester.tap(find.text('任意技师'));
await tester.pump(Duration(seconds: 1));
// When — 选择时间
await tester.tap(find.text('14:00'));
await tester.pump(Duration(seconds: 1));
// When — 输入手机号
await tester.enterText(
find.byType(TextField),
'1234567890',
);
await tester.tap(find.text('确认预约'));
await tester.pump(Duration(seconds: 2));
// Then — 验证预约成功
expect(find.text('预约成功'), findsOneWidget);
});
}
| 工具 | 特点 | 适用场景 |
|---|---|---|
integration_test |
Flutter 官方,稳定可靠 | 基础 E2E 测试,CI/CD 集成 |
| Patrol | 支持原生交互(权限弹窗、通知),测试隔离 | 需要操作系统级交互的测试 |
| Firebase Test Lab | 真机云测试,多设备并行 | 发布前多设备兼容性验证 |
| 层级 | 理想状态 | Guest App 现状 | 状态 |
|---|---|---|---|
| CUJ 旅程地图 | 有文档,标注关键路径和优先级 | 缺失 — 没有用户旅程文档 | ❌ 缺失 |
| User Flow 图 | 有流程图,覆盖所有分支 | router.dart 隐含了流程,无可视化文档 | ⚠️ 隐含 |
| BDD 场景 | Gherkin 格式场景文档 | 缺失 — 没有结构化的场景描述 | ❌ 缺失 |
| E2E 测试 | 覆盖所有 CUJ 的自动化测试 | 有部分 integration_test,覆盖不全 | ⚠️ 部分 |
| CI 集成 | PR 自动跑 CUJ 测试 | 可本地跑,CI 配置待完善 | ⚠️ 部分 |
| 优先级 | CUJ | 路径 | 业务价值 |
|---|---|---|---|
| P0 | 新用户单服务预约 | 首页 → 选服务 → 选技师 → 选时间 → 手机登录 → 完成 | 核心收入路径 |
| P0 | 深度链接预约 | 分享链接 → 打开 App → 服务列表 → 预约 | 获客转化 |
| P1 | 多服务购物车结账 | 选服务 × N → 购物车 → 逐项配置 → 结账 | 客单价提升 |
| P1 | 邮箱注册 + 登录 | 注册 → 验证邮箱 → 登录 | 用户留存 |
| P2 | 查看/管理预约 | 我的预约 → 查看详情 → 取消/改期 | 用户体验 |
| P2 | 账户管理 | 个人资料 → 修改信息 / 修改密码 | 用户体验 |
| P3 | 语言切换 | 设置 → 切换中英文 → 全局生效 | 国际化支持 |
| 概念 | 形式 | 关注点 | 产出人 | 你需要吗? |
|---|---|---|---|---|
| Storyboard 故事板 |
漫画式插图叙事 (场景 + 人物 + 情感) |
用户在什么场景下使用产品 | UX 设计师 | 🟡 可选 — 适合做用户研究、向非技术人员展示 |
| User Flow 用户流程图 |
流程图 (页面 → 判断 → 页面) |
页面跳转逻辑和分支条件 | 设计师/工程师 | 🟢 必要 — 直接指导开发和测试 |
| Journey Map 旅程地图 |
时间线 + 情感曲线 (阶段、触点、痛点) |
用户全流程体验和改进机会 | 产品经理/UX | 🟢 推荐 — 识别哪些路径最关键 |
| BDD Scenario 行为场景 |
Given-When-Then 文本 | 什么行为算"正确" | 产品 + 工程协作 | 🟢 推荐 — 作为测试的规格说明 |
| 方法 | 起点 | 格式 | 适用 |
|---|---|---|---|
| TDD | 技术规格 | assert / expect | 单元测试、函数级别 |
| BDD | 用户行为 | Given-When-Then | E2E 测试、验收测试 |
| 传统测试 | 测试计划文档 | 测试用例表格 | QA 手动测试 |
| 步骤 | 产出 | 格式 | 存放位置 |
|---|---|---|---|
| 1. 梳理 CUJ | 5-7 条关键用户旅程 | Markdown 表格 | notes/guest-app-cuj.html |
| 2. 画 User Flow | 每条 CUJ 的流程图 | Mermaid 图 | 同上,或 specs/ 目录 |
| 3. 写 BDD 场景 | 每条 CUJ 2-3 个场景 | Given-When-Then 文本 | 同上(不需要 .feature 文件) |
| 4. 实现 E2E | 对应的 integration_test | Dart 测试代码 | integration_test/ |
| 主题 | 来源 | 链接 |
|---|---|---|
| User Journey vs User Flow | Nielsen Norman Group | NN/g 文章 |
| Storyboard 定义 | Interaction Design Foundation | IxDF 文章 |
| BDD 方法论 | Scaled Agile Framework | SAFe BDD 指南 |
| BDD 验收标准实践 | Thoughtworks | Thoughtworks 博客 |
| CUJ 测试覆盖 | Google (TD Commons) | Google CUJ 论文 |
| User Journey Testing | BugBug | 测试指南 |
| Critical User Journey 实践 | Product School | CUJ 实例 |
| Flutter E2E 测试 | Flutter 官方 | Integration Tests 文档 |
| Patrol 测试框架 | LeanCode | Patrol 官网 |
| Agile Storyboard 方法 | Roman Pichler | Agile 场景与故事板 |
Generated: 2026-02-06 | Project: QQ Nails Guest Mobile App | 查看方式: npm run notes → 打开 guest-app-ux-testing-methodology.html