# 自动化测试文档 本目录包含基于 Playwright 的端到端 (E2E) 自动化测试脚本。 ## 目录结构 ``` tests/ ├── README.md # 本文档 ├── constitution.test.js # 体质分析功能测试 ├── profile.test.js # "我的"页面功能测试 ├── health-profile-complete.test.js # 健康档案完整功能测试(推荐) └── screenshots/ # 测试截图目录 ├── constitution-result.png # 体质测试结果截图 ├── profile-page.png # 我的页面截图 ├── hp-basic-*.png # 基础信息编辑截图 ├── hp-lifestyle-*.png # 生活习惯编辑截图 ├── hp-medical-*.png # 病史记录添加截图 └── hp-allergy-*.png # 过敏记录添加截图 ``` ## 环境准备 ### 1. 安装依赖 ```bash # 安装 Playwright npm install playwright # 安装浏览器(首次运行) npx playwright install chromium ``` ### 2. 启动应用 测试前需要确保前端和后端服务都在运行: ```bash # 终端1: 启动后端服务 cd server go run main.go # 终端2: 启动前端服务 cd app npm start # 或 npx expo start --web ``` 确保应用可以通过 `http://localhost:8081` 访问。 ## 运行测试 ### 运行所有测试 ```bash # 从项目根目录运行 node tests/constitution.test.js # 体质分析测试 node tests/profile.test.js # "我的"页面测试 node tests/health-profile-complete.test.js # 健康档案完整测试(推荐) ``` ### 运行体质分析测试 ```bash node tests/constitution.test.js ``` ### 运行"我的"页面测试 ```bash node tests/profile.test.js ``` ### 运行健康档案完整测试(推荐) ```bash node tests/health-profile-complete.test.js ``` ### 测试配置 测试脚本中的配置项(位于文件开头): ```javascript const APP_URL = "http://localhost:8081"; // 应用地址 const TEST_PHONE = "13800138000"; // 测试手机号 const TEST_CODE = "123456"; // 测试验证码 ``` ## 测试脚本说明 ### constitution.test.js - 体质分析功能测试 **测试流程:** 1. **登录** - 使用测试账号登录应用 2. **导航** - 进入"体质"Tab 3. **开始测试** - 点击"开始测试"按钮 4. **回答问题** - 自动回答 67 道体质问卷题目 5. **提交** - 提交答案获取结果 6. **验证结果** - 检查结果页面各元素 7. **重新测评** - 测试重新测评功能 **验证项目:** | 检查项 | 说明 | | ---------------- | ---------------------- | | 登录 | 验证登录流程正常 | | 导航到体质页面 | 验证 Tab 导航正常 | | 进入测试页面 | 验证开始测试按钮可点击 | | 回答所有问题 | 验证 67 道题目全部完成 | | 提交并查看结果 | 验证提交后跳转到结果页 | | 体质分析报告标题 | 验证结果页标题显示 | | 主体质名称 | 验证显示体质类型名称 | | 体质得分卡片 | 验证得分区域显示 | | 体质特征卡片 | 验证特征区域显示 | | 调理建议卡片 | 验证建议区域显示 | | 咨询 AI 助手按钮 | 验证功能按钮显示 | | 重新测评按钮 | 验证重新测评按钮显示 | | 重新测评导航 | 验证重新测评功能正常 | **输出示例:** ``` ═══════════════════════════════════════════════════════════ 体质分析功能自动化测试 ═══════════════════════════════════════════════════════════ 打开应用... 【步骤1】检查登录状态... 执行登录流程... ✓ 登录 【步骤2】导航到体质Tab... ✓ 导航到体质页面 【步骤3】开始体质测试... ✓ 进入测试页面 【步骤4】回答问题... 回答第 1/67 题... 回答第 2/67 题... ... 回答第 67/67 题... 所有题目已回答,准备提交... ✓ 回答所有问题: 共 67 题 【步骤5】提交测试... ✓ 提交并查看结果 【步骤6】验证结果页面内容... ✓ 体质分析报告标题 ✓ 主体质名称 ✓ 体质得分卡片 ✓ 体质特征卡片 ✓ 调理建议卡片 ✓ 咨询AI助手按钮 ✓ 重新测评按钮 检测到体质类型: 特禀质 【步骤7】测试重新测评功能... ✓ 重新测评导航 ═══════════════════════════════════════════════════════════ 测试结果摘要 ═══════════════════════════════════════════════════════════ 通过: 13 失败: 0 ─────────────────────────────────────────────────────────── ✓ 登录 ✓ 导航到体质页面 ✓ 进入测试页面 ✓ 回答所有问题 - 共 67 题 ✓ 提交并查看结果 ✓ 体质分析报告标题 ✓ 主体质名称 ✓ 体质得分卡片 ✓ 体质特征卡片 ✓ 调理建议卡片 ✓ 咨询AI助手按钮 ✓ 重新测评按钮 ✓ 重新测评导航 ═══════════════════════════════════════════════════════════ ``` ### profile.test.js - "我的"页面功能测试 **测试流程:** 1. **登录** - 使用测试账号登录 2. **导航** - 进入"我的"Tab 页面 3. **用户信息显示** - 验证昵称、手机号、编辑按钮 4. **编辑昵称** - 打开弹窗、修改昵称、保存 5. **适老模式** - 验证开关功能 6. **健康管理菜单** - 验证四个菜单项显示 7. **健康档案导航** - 跳转到健康档案页面 8. **健康档案编辑功能** - 测试基础信息/生活习惯编辑、病史/家族病史/过敏记录新增 9. **用药/治疗记录** - 打开弹窗查看 10. **关于我们** - 打开弹窗查看 11. **退出登录** - 点击并验证确认弹窗 **验证项目:** | 检查项 | 说明 | | -------------------- | -------------------- | | 登录 | 使用测试账号登录 | | 导航到"我的"页面 | Tab 导航正常 | | 用户昵称显示 | 显示用户昵称 | | 手机号显示 | 显示手机号 | | 编辑按钮显示 | 编辑图标可见 | | 编辑弹窗打开 | 点击编辑打开弹窗 | | 保存昵称 | 修改昵称并保存成功 | | 适老模式卡片显示 | 适老模式开关可见 | | 适老模式开关 | 开关可切换 | | 健康档案菜单显示 | 健康档案菜单可见 | | 用药记录菜单显示 | 用药记录菜单可见 | | 体质报告菜单显示 | 体质报告菜单可见 | | 对话历史菜单显示 | 对话历史菜单可见 | | 健康档案页面打开 | 导航到健康档案页 | | 基础信息卡片显示 | 基础信息卡片可见 | | 基础信息编辑弹窗打开 | 点击编辑按钮打开弹窗 | | 生活习惯卡片显示 | 生活习惯卡片可见 | | 病史记录卡片显示 | 病史记录卡片可见 | | 病史记录新增弹窗打开 | 点击新增按钮打开弹窗 | | 家族病史卡片显示 | 家族病史卡片可见 | | 过敏记录卡片显示 | 过敏记录卡片可见 | | 用药记录弹窗打开 | 点击打开用药记录弹窗 | | 退出登录按钮显示 | 退出按钮可见 | **运行命令:** ```bash node tests/profile.test.js ``` --- ### health-profile-complete.test.js - 健康档案完整功能测试(推荐) 这是最全面的健康档案测试脚本,覆盖所有可编辑字段的输入和保存验证。 **测试范围:** | 功能模块 | 测试字段数 | 测试内容 | | -------- | ---------- | ------------------------------------------------------------------------------------------ | | 基础信息 | 9 个字段 | 姓名、性别、出生日期、身高、体重、血型、职业、婚姻状况、地区 | | 生活习惯 | 10 个字段 | 入睡时间、起床时间、睡眠质量、三餐规律、饮食偏好、日饮水量、运动频率、运动类型、吸烟、饮酒 | | 病史记录 | 5 个字段 | 疾病名称、疾病类型、诊断日期、治疗状态、备注 | | 家族病史 | 3 个字段 | 亲属关系、疾病名称、备注 | | 过敏记录 | 4 个字段 | 过敏类型、过敏原、严重程度、过敏反应描述 | **测试流程:** 1. **登录** - 使用测试账号登录 2. **导航** - 进入健康档案页面 3. **基础信息编辑** - 测试所有 9 个字段的输入和保存 4. **生活习惯编辑** - 测试所有 10 个字段的输入和保存 5. **病史记录添加** - 测试添加新病史记录(5 个字段) 6. **家族病史添加** - 测试添加新家族病史(3 个字段) 7. **过敏记录添加** - 测试添加新过敏记录(4 个字段) 8. **数据验证** - 刷新页面后验证所有数据是否正确保存 **验证项目:** | 检查项 | 说明 | | ----------------------- | ------------------------- | | 导航到健康档案页面 | Tab 和菜单导航正常 | | 打开基础信息编辑弹窗 | 编辑按钮可点击 | | 基础信息-姓名输入 | TextInput 输入正常 | | 基础信息-性别选择 | SegmentedButtons 选择正常 | | 基础信息-出生日期输入 | 日期格式输入正常 | | 基础信息-身高/体重输入 | 数字输入正常 | | 基础信息-血型输入 | 文本输入正常 | | 基础信息-职业输入 | 文本输入正常 | | 基础信息-婚姻状况选择 | SegmentedButtons 选择正常 | | 基础信息-地区输入 | 文本输入正常 | | 基础信息-保存成功 | API 调用成功,显示提示 | | 基础信息-保存后数据显示 | 页面正确显示保存的数据 | | 生活习惯-所有字段测试 | 同上,共 10 个字段 | | 病史记录-添加新记录 | 弹窗、输入、添加、显示 | | 家族病史-添加新记录 | 弹窗、输入、添加、显示 | | 过敏记录-添加新记录 | 弹窗、输入、添加、显示 | | 刷新后数据验证 | 页面刷新后数据仍然正确 | **输出示例:** ``` ═══════════════════════════════════════════════════════════ 健康档案完整功能自动化测试 ═══════════════════════════════════════════════════════════ 测试范围: - 基础信息: 9个字段(姓名、性别、出生日期、身高、体重、血型、职业、婚姻、地区) - 生活习惯: 10个字段(入睡时间、起床时间、睡眠质量、三餐规律、饮食偏好、 日饮水量、运动频率、运动类型、吸烟、饮酒) - 病史记录: 5个字段(疾病名称、疾病类型、诊断日期、治疗状态、备注) - 家族病史: 3个字段(亲属关系、疾病名称、备注) - 过敏记录: 4个字段(过敏类型、过敏原、严重程度、过敏反应描述) 【步骤2】测试基础信息编辑(9个字段)... ✓ 打开基础信息编辑弹窗 ✓ 基础信息-姓名输入 ✓ 基础信息-性别选择 ✓ 基础信息-出生日期输入 ✓ 基础信息-身高输入 ✓ 基础信息-体重输入 ✓ 基础信息-血型输入 ✓ 基础信息-职业输入 ✓ 基础信息-婚姻状况选择 ✓ 基础信息-地区输入 ✓ 基础信息-保存成功 - 显示保存成功提示 ✓ 基础信息-保存后姓名显示 ✓ 基础信息-保存后地区显示 ✓ 基础信息-保存后职业显示 ... (更多测试输出) ═══════════════════════════════════════════════════════════ 测试结果摘要 ═══════════════════════════════════════════════════════════ 通过: 58 失败: 0 ═══════════════════════════════════════════════════════════ ``` **运行命令:** ```bash node tests/health-profile-complete.test.js ``` --- ## 编写新测试 ### 基础模板 ```javascript const { chromium } = require("playwright"); const APP_URL = "http://localhost:8081"; // 测试结果统计 const testResults = { passed: 0, failed: 0, tests: [] }; function logTest(name, passed, detail = "") { const status = passed ? "✓" : "✗"; console.log(`${status} ${name}${detail ? ": " + detail : ""}`); testResults.tests.push({ name, passed, detail }); if (passed) testResults.passed++; else testResults.failed++; } async function runTests() { const browser = await chromium.launch({ headless: false }); const page = await browser.newPage(); // 监听错误 page.on("console", (msg) => { if (msg.type() === "error") { console.log("[Console Error]", msg.text()); } }); page.on("pageerror", (error) => { console.log("[Page Error]", error.message); }); try { await page.goto(APP_URL); await page.waitForTimeout(2000); // 添加测试步骤... } catch (error) { console.error("测试中断:", error.message); await page.screenshot({ path: "tests/screenshots/error.png" }); } finally { // 打印结果 console.log(`\n通过: ${testResults.passed} 失败: ${testResults.failed}`); await browser.close(); process.exit(testResults.failed > 0 ? 1 : 0); } } runTests(); ``` ### 常用操作 #### 点击元素 ```javascript // 方式1: 文本定位 await page.locator("text=按钮文字").click(); // 方式2: 角色定位 await page.getByRole("button", { name: "提交" }).click(); // 方式3: 坐标点击 (适用于 React Native Web) const pos = await page.evaluate(() => { const el = document.querySelector("text=按钮"); const rect = el.getBoundingClientRect(); return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 }; }); await page.mouse.click(pos.x, pos.y); // 强制点击 (忽略可见性检查) await page.locator("text=按钮").click({ force: true }); ``` #### 输入文本 ```javascript const input = page.locator("input").first(); await input.fill("输入内容"); ``` #### 等待元素 ```javascript // 等待元素可见 await page.locator("text=内容").waitFor({ state: "visible", timeout: 5000 }); // 检查元素是否可见 const visible = await page .locator("text=内容") .isVisible({ timeout: 2000 }) .catch(() => false); ``` #### 截图 ```javascript await page.screenshot({ path: "tests/screenshots/截图名.png" }); ``` ## 注意事项 ### React Native Web 特殊处理 由于 React Native Web 的渲染机制,某些标准选择器可能不起作用: 1. **优先使用坐标点击** - 通过 `evaluate` 获取元素位置后用 `mouse.click` 2. **使用 force 选项** - 某些元素可能被遮挡,使用 `{ force: true }` 3. **增加等待时间** - React Native 动画可能需要更长时间完成 ### 测试账号 测试使用的账号: - 手机号: `13800138000` - 验证码: `123456` ### 截图目录 测试截图保存在 `tests/screenshots/` 目录下,包括: - 测试过程截图 - 错误截图(当测试失败时自动保存) ## CI/CD 集成 可以在 CI/CD 流程中使用 headless 模式运行测试: ```javascript // 修改 browser launch 配置 const browser = await chromium.launch({ headless: true, // 无头模式 }); ``` 退出码说明: - `0` - 所有测试通过 - `1` - 存在测试失败