testing-team
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTesting Team
测试团队
Elite QA and playtesting specialists ensuring every build is fun, polished, and production-ready.
精英QA与游戏测试专家,确保每个构建版本趣味十足、打磨精良且具备上线条件。
Team Composition
团队构成
| Role | Domain | Focus |
|---|---|---|
| QA Lead | Test strategy, release gates | Overall quality ownership |
| Playtest Coordinator | Fun factor, game feel | Player experience sessions |
| UX Researcher | Usability, player feedback | Data-driven UX decisions |
| Code Quality Engineer | Linting, patterns, refactoring | Clean, maintainable code |
| Test Automation Engineer | Unit, integration, e2e tests | Automated test coverage |
| Performance Analyst | Load testing, profiling | 60fps, fast loads |
| Accessibility Specialist | WCAG, inclusive design | Everyone can play |
| 角色 | 领域 | 核心关注 |
|---|---|---|
| QA Lead | 测试策略、发布准入 | 整体质量管控 |
| Playtest Coordinator | 趣味性、游戏手感 | 玩家体验测试环节 |
| UX Researcher | 易用性、玩家反馈 | 数据驱动的UX决策 |
| Code Quality Engineer | 代码检查、代码模式、重构 | 简洁可维护的代码 |
| Test Automation Engineer | 单元、集成、E2E测试 | 自动化测试覆盖率 |
| Performance Analyst | 负载测试、性能分析 | 稳定60fps、快速加载 |
| Accessibility Specialist | WCAG、包容性设计 | 全玩家群体适配 |
Playtesting Framework
游戏测试框架
The Fun Factor Checklist
趣味性检查清单
Before any build ships, validate these pillars:
□ ENGAGEMENT
□ Does the core loop feel satisfying?
□ Is there clear moment-to-moment feedback?
□ Do players want "one more turn"?
□ CLARITY
□ Do players understand what to do?
□ Are goals and progress visible?
□ Is the UI intuitive without tutorial?
□ PROGRESSION
□ Does difficulty ramp appropriately?
□ Are rewards meaningful and well-paced?
□ Is there a sense of mastery over time?
□ JUICE
□ Do interactions feel responsive?
□ Are wins celebrated appropriately?
□ Does the game have "weight" and polish?
□ FAIRNESS
□ Do losses feel fair (not random/cheap)?
□ Is RNG transparent to players?
□ Are comeback mechanics present?在任何构建版本发布前,验证以下核心维度:
□ ENGAGEMENT
□ 核心循环是否令人满意?
□ 是否有清晰的即时反馈?
□ 玩家是否有“再来一局”的意愿?
□ CLARITY
□ 玩家是否清楚该做什么?
□ 目标与进度是否可见?
□ 无需教程UI是否直观易懂?
□ PROGRESSION
□ 难度提升是否合理?
□ 奖励是否有意义且节奏得当?
□ 随着游戏推进是否有掌控感?
□ JUICE
□ 交互是否响应及时?
□ 胜利反馈是否到位?
□ 游戏是否具备“分量感”与打磨质感?
□ FAIRNESS
□ 失败是否公平(非随机/廉价)?
□ RNG机制是否对玩家透明?
□ 是否有逆风翻盘机制?Playtest Session Structure
游戏测试环节流程
SESSION TEMPLATE (45-60 minutes)
1. SETUP (5 min)
- Fresh build, cleared save data
- Screen recording enabled
- Observer notes template ready
2. FIRST-TIME USER EXPERIENCE (15 min)
- No guidance, observe natural behavior
- Note: Where do they get stuck?
- Note: What do they try first?
- Note: Facial expressions, body language
3. GUIDED EXPLORATION (15 min)
- Introduce features they missed
- Ask: "What do you think this does?"
- Ask: "How did that feel?"
4. FREE PLAY (10 min)
- Let them play naturally
- Note: What do they gravitate toward?
- Note: What do they avoid?
5. DEBRIEF (10 min)
- "What was most fun?"
- "What was frustrating?"
- "Would you play again? Why?"
- "Rate 1-10: How fun was this?"SESSION TEMPLATE (45-60分钟)
1. 准备阶段(5分钟)
- 全新构建版本,清空存档数据
- 开启屏幕录制
- 准备好观察者记录模板
2. 首次用户体验(15分钟)
- 不提供引导,观察玩家自然行为
- 记录:玩家在哪里卡住?
- 记录:玩家首先尝试什么?
- 记录:面部表情、肢体语言
3. 引导式探索(15分钟)
- 介绍玩家未发现的功能
- 提问:“你认为这个功能是做什么的?”
- 提问:“这个操作体验如何?”
4. 自由游玩(10分钟)
- 让玩家自由游玩
- 记录:玩家倾向于玩什么?
- 记录:玩家避开什么内容?
5. 复盘访谈(10分钟)
- “最有趣的部分是什么?”
- “最令人沮丧的部分是什么?”
- “你会再次游玩吗?为什么?”
- “评分1-10:这个游戏的趣味性如何?”Player Experience Metrics
玩家体验指标
| Metric | Target | How to Measure |
|---|---|---|
| Session Length | >10 min | Analytics |
| Return Rate | >40% D1 | Analytics |
| Core Loop Completion | >80% | Funnel tracking |
| Rage Quit Rate | <5% | Session end analysis |
| Fun Rating | >7/10 | Post-session survey |
| Confusion Events | <3/session | Observer notes |
| "Aha!" Moments | >2/session | Observer notes |
| 指标 | 目标 | 测量方式 |
|---|---|---|
| 会话时长 | >10分钟 | 数据分析工具 |
| 次日留存率 | >40% | 数据分析工具 |
| 核心循环完成率 | >80% | 转化漏斗追踪 |
| 愤怒退出率 | <5% | 会话结束分析 |
| 趣味评分 | >7/10 | 测试后问卷 |
| 困惑事件数 | <3次/会话 | 观察者记录 |
| “顿悟”时刻 | >2次/会话 | 观察者记录 |
Code Quality Standards
代码质量标准
Linting & Formatting
代码检查与格式化
json
// .eslintrc.json for the project
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "warn",
"react-hooks/exhaustive-deps": "error",
"no-console": ["warn", { "allow": ["warn", "error"] }],
"complexity": ["warn", 10],
"max-lines-per-function": ["warn", 50]
}
}json
// biome.json alternative
{
"linter": {
"rules": {
"complexity": {
"noExcessiveCognitiveComplexity": {
"level": "warn",
"options": { "maxAllowedComplexity": 15 }
}
},
"correctness": {
"useExhaustiveDependencies": "error"
}
}
}
}json
// 项目的.eslintrc.json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "warn",
"react-hooks/exhaustive-deps": "error",
"no-console": ["warn", { "allow": ["warn", "error"] }],
"complexity": ["warn", 10],
"max-lines-per-function": ["warn", 50]
}
}json
// 替代方案biome.json
{
"linter": {
"rules": {
"complexity": {
"noExcessiveCognitiveComplexity": {
"level": "warn",
"options": { "maxAllowedComplexity": 15 }
}
},
"correctness": {
"useExhaustiveDependencies": "error"
}
}
}
}Code Review Checklist
代码评审检查清单
markdown
undefinedmarkdown
undefinedPR Review Template
PR评审模板
Functionality
功能验证
- Code does what the PR description claims
- Edge cases are handled
- Error states are managed gracefully
- No regressions in existing features
- 代码实现与PR描述一致
- 边缘场景已处理
- 错误状态已优雅处理
- 现有功能无回归问题
Code Quality
代码质量
- Functions are small and focused (<50 lines)
- Names are clear and descriptive
- No magic numbers (use constants)
- Complex logic has comments explaining "why"
- No duplicate code (DRY)
- TypeScript types are specific (no )
any
- 函数小巧且职责单一(<50行)
- 命名清晰且具备描述性
- 无魔法数字(使用常量)
- 复杂逻辑有注释说明“为什么”
- 无重复代码(遵循DRY原则)
- TypeScript类型明确(无)
any
Performance
性能
- No unnecessary re-renders (React)
- Heavy computations are memoized
- No memory leaks (cleanup in useEffect)
- Bundle size impact is acceptable
- 无不必要的重渲染(React)
- 复杂计算已做缓存
- 无内存泄漏(useEffect中已清理)
- 对包体积的影响可接受
Testing
测试
- New code has tests
- Tests are meaningful (not just coverage)
- Tests run fast (<100ms each)
- 新增代码已有对应测试
- 测试具备实际意义(非仅为了覆盖率)
- 测试执行快速(每个<100ms)
Game-Specific
游戏特定检查
- Game feel is preserved
- Animation timing matches design
- Sound effects trigger correctly
- Mobile/touch interactions work
undefined- 游戏手感得以保留
- 动画时长符合设计要求
- 音效触发正确
- 移动端/触控交互正常
undefinedRefactoring Patterns
重构模式
When to Refactor:
RED FLAGS:
- Function > 50 lines
- File > 300 lines
- Cyclomatic complexity > 10
- More than 3 levels of nesting
- Repeated code blocks
- "Utils" file growing unbounded
- Props drilling > 3 levels
- useEffect with 5+ dependenciesSafe Refactoring Process:
1. Ensure tests exist (write them first if not)
2. Make ONE change at a time
3. Run tests after each change
4. Commit working states frequently
5. Review diff before finalizing重构触发条件:
预警信号:
- 函数行数>50
- 文件行数>300
- 圈复杂度>10
- 嵌套层级>3层
- 存在重复代码块
- “Utils”文件无限制膨胀
- Props透传层级>3层
- useEffect依赖项>5个安全重构流程:
1. 确保已有测试(若没有则先编写)
2. 每次仅做一处修改
3. 每次修改后运行测试
4. 频繁提交可工作的版本状态
5. 最终定稿前审查代码差异Test Automation
自动化测试
Test Pyramid for Games
游戏测试金字塔
╱╲
╱ ╲ E2E Tests (Playwright)
╱ 10%╲ - Full user flows
╱──────╲ - Critical paths only
╱ ╲
╱ 20% ╲ Integration Tests
╱────────────╲ - Component interactions
╱ ╲ - Store + UI together
╱ 70% ╲ Unit Tests (Vitest)
╲────────────────╱ - Pure functions
- Game logic
- Utilities ╱╲
╱ ╲ E2E Tests (Playwright)
╱ 10%╲ - 完整用户流程
╱──────╲ - 仅覆盖关键路径
╱ ╲
╱ 20% ╲ Integration Tests
╱────────────╲ - 组件间交互
╱ ╲ - Store与UI联合测试
╱ 70% ╲ Unit Tests (Vitest)
╲────────────────╱ - 纯函数测试
- 游戏逻辑测试
- 工具函数测试Unit Test Examples
单元测试示例
typescript
// Game logic tests
import { describe, it, expect } from 'vitest';
import { calculateBet, hexDistance, spreadTrouble } from './game-engine';
describe('calculateBet', () => {
it('returns 0 for fold', () => {
expect(calculateBet(1000, 'fold')).toBe(0);
});
it('returns 10% of coins for call', () => {
expect(calculateBet(1000, 'call')).toBe(100);
});
it('returns all coins for all_in', () => {
expect(calculateBet(1000, 'all_in')).toBe(1000);
});
it('floors fractional amounts', () => {
expect(calculateBet(1005, 'call')).toBe(100);
});
});
describe('hexDistance', () => {
it('returns 0 for same hex', () => {
const hex = { q: 0, r: 0, s: 0 };
expect(hexDistance(hex, hex)).toBe(0);
});
it('calculates adjacent hex distance as 1', () => {
const a = { q: 0, r: 0, s: 0 };
const b = { q: 1, r: -1, s: 0 };
expect(hexDistance(a, b)).toBe(1);
});
});typescript
// 游戏逻辑测试
import { describe, it, expect } from 'vitest';
import { calculateBet, hexDistance, spreadTrouble } from './game-engine';
describe('calculateBet', () => {
it('returns 0 for fold', () => {
expect(calculateBet(1000, 'fold')).toBe(0);
});
it('returns 10% of coins for call', () => {
expect(calculateBet(1000, 'call')).toBe(100);
});
it('returns all coins for all_in', () => {
expect(calculateBet(1000, 'all_in')).toBe(1000);
});
it('floors fractional amounts', () => {
expect(calculateBet(1005, 'call')).toBe(100);
});
});
describe('hexDistance', () => {
it('returns 0 for same hex', () => {
const hex = { q: 0, r: 0, s: 0 };
expect(hexDistance(hex, hex)).toBe(0);
});
it('calculates adjacent hex distance as 1', () => {
const a = { q: 0, r: 0, s: 0 };
const b = { q: 1, r: -1, s: 0 };
expect(hexDistance(a, b)).toBe(1);
});
});Component Test Examples
组件测试示例
typescript
// Component tests with Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import { ResourceBar } from './ResourceBar';
describe('ResourceBar', () => {
it('displays the resource value', () => {
render(<ResourceBar value={500} label="Coins" icon="🪙" />);
expect(screen.getByText('500')).toBeInTheDocument();
});
it('animates on value change', async () => {
const { rerender } = render(<ResourceBar value={500} label="Coins" />);
rerender(<ResourceBar value={600} label="Coins" />);
// Check for delta indicator
expect(screen.getByText('+100')).toBeInTheDocument();
});
});typescript
// 使用Testing Library的组件测试
import { render, screen, fireEvent } from '@testing-library/react';
import { ResourceBar } from './ResourceBar';
describe('ResourceBar', () => {
it('displays the resource value', () => {
render(<ResourceBar value={500} label="Coins" icon="🪙" />);
expect(screen.getByText('500')).toBeInTheDocument();
});
it('animates on value change', async () => {
const { rerender } = render(<ResourceBar value={500} label="Coins" />);
rerender(<ResourceBar value={600} label="Coins" />);
// 检查差值指示器
expect(screen.getByText('+100')).toBeInTheDocument();
});
});E2E Test Examples
E2E测试示例
typescript
// Playwright e2e tests
import { test, expect } from '@playwright/test';
test.describe('Game Flow', () => {
test('complete a full day cycle', async ({ page }) => {
await page.goto('/game');
// Verify initial state
await expect(page.getByText('Day 1')).toBeVisible();
await expect(page.getByText('Morning')).toBeVisible();
// Advance through phases
await page.getByRole('button', { name: /advance/i }).click();
await expect(page.getByText('Action')).toBeVisible();
// Place a bet
await page.getByTestId('water-pot').click();
await page.getByRole('button', { name: /call/i }).click();
// Continue to resolution
await page.getByRole('button', { name: /advance/i }).click();
await expect(page.getByText('Resolution')).toBeVisible();
// Verify score changed
const score = await page.getByTestId('score').textContent();
expect(parseInt(score || '0')).toBeGreaterThan(0);
});
test('hex interaction works', async ({ page }) => {
await page.goto('/game');
// Click a hex
await page.getByTestId('hex-0-0').click();
// Verify selection
await expect(page.getByTestId('hex-0-0')).toHaveClass(/selected/);
});
});typescript
// Playwright E2E测试
import { test, expect } from '@playwright/test';
test.describe('Game Flow', () => {
test('complete a full day cycle', async ({ page }) => {
await page.goto('/game');
// 验证初始状态
await expect(page.getByText('Day 1')).toBeVisible();
await expect(page.getByText('Morning')).toBeVisible();
// 推进阶段
await page.getByRole('button', { name: /advance/i }).click();
await expect(page.getByText('Action')).toBeVisible();
// 下注
await page.getByTestId('water-pot').click();
await page.getByRole('button', { name: /call/i }).click();
// 继续到结算阶段
await page.getByRole('button', { name: /advance/i }).click();
await expect(page.getByText('Resolution')).toBeVisible();
// 验证分数已变化
const score = await page.getByTestId('score').textContent();
expect(parseInt(score || '0')).toBeGreaterThan(0);
});
test('hex interaction works', async ({ page }) => {
await page.goto('/game');
// 点击六边形格子
await page.getByTestId('hex-0-0').click();
// 验证已选中
await expect(page.getByTestId('hex-0-0')).toHaveClass(/selected/);
});
});Test Commands
测试命令
bash
undefinedbash
undefinedRun all tests
运行所有测试
bun run test
bun run test
Watch mode
监听模式
bun run test:watch
bun run test:watch
Coverage report
生成覆盖率报告
bun run test:coverage
bun run test:coverage
E2E tests
运行E2E测试
bun run test:e2e
bun run test:e2e
Type checking
类型检查
bun run check-types
undefinedbun run check-types
undefinedPerformance Testing
性能测试
Metrics & Budgets
指标与预算
| Metric | Budget | Tool |
|---|---|---|
| First Contentful Paint | <1.5s | Lighthouse |
| Time to Interactive | <3s | Lighthouse |
| Frame Rate | 60fps stable | Chrome DevTools |
| JS Bundle | <200KB gzipped | Bundlesize |
| Memory (gameplay) | <100MB | Chrome DevTools |
| Animation Jank | 0 dropped frames | Performance panel |
| 指标 | 预算 | 工具 |
|---|---|---|
| 首次内容绘制 | <1.5秒 | Lighthouse |
| 可交互时间 | <3秒 | Lighthouse |
| 帧率 | 稳定60fps | Chrome DevTools |
| JS包体积 | <200KB(gzip压缩后) | Bundlesize |
| 内存占用(游戏中) | <100MB | Chrome DevTools |
| 动画卡顿 | 无丢帧 | 性能面板 |
Performance Checklist
性能检查清单
markdown
undefinedmarkdown
undefinedPer-Build Performance Review
每个构建版本的性能评审
Initial Load
初始加载
- Bundle size within budget
- No render-blocking resources
- Images optimized and lazy-loaded
- Fonts preloaded
- 包体积符合预算
- 无阻塞渲染的资源
- 图片已优化且懒加载
- 字体已预加载
Runtime
运行时
- 60fps during gameplay
- No memory leaks (check after 10 min play)
- Animations use GPU (transform/opacity only)
- Large lists are virtualized
- 游戏过程中稳定60fps
- 无内存泄漏(游玩10分钟后检查)
- 动画使用GPU加速(仅transform/opacity)
- 大型列表已做虚拟化处理
Mobile
移动端
- Touch response <100ms
- No janky scrolling
- Battery usage reasonable
undefined- 触控响应<100ms
- 滚动无卡顿
- 电池使用合理
undefinedBuild Validation Gates
构建验证准入标准
Pre-Merge Checklist
合并前检查清单
yaml
undefinedyaml
undefinedCI Pipeline Gates
CI流水线准入标准
quality_gates:
-
name: "Lint" command: "bun run lint" required: true
-
name: "Type Check" command: "bun run check-types" required: true
-
name: "Unit Tests" command: "bun run test" required: true coverage_threshold: 70%
-
name: "Build" command: "bun run build" required: true
-
name: "Bundle Size" max_size: "200KB" required: true
-
name: "E2E Smoke" command: "bun run test:e2e --grep @smoke" required: true
undefinedquality_gates:
-
name: "Lint" command: "bun run lint" required: true
-
name: "Type Check" command: "bun run check-types" required: true
-
name: "Unit Tests" command: "bun run test" required: true coverage_threshold: 70%
-
name: "Build" command: "bun run build" required: true
-
name: "Bundle Size" max_size: "200KB" required: true
-
name: "E2E Smoke" command: "bun run test:e2e --grep @smoke" required: true
undefinedRelease Readiness
发布就绪检查
markdown
undefinedmarkdown
undefinedRelease Checklist
发布检查清单
Code Quality
代码质量
- All tests passing
- No TypeScript errors
- No ESLint errors
- Code coverage >70%
- 所有测试通过
- 无TypeScript错误
- 无ESLint错误
- 代码覆盖率>70%
Functionality
功能
- All features work as designed
- No P0/P1 bugs open
- Regression suite passing
- 所有功能按设计工作
- 无P0/P1级别Bug
- 回归测试套件通过
Performance
性能
- Lighthouse score >90
- 60fps confirmed
- Load time <3s
- Lighthouse评分>90
- 已确认稳定60fps
- 加载时间<3秒
Player Experience
玩家体验
- Playtest feedback addressed
- Fun rating >7/10
- No confusion points remaining
- 游戏测试反馈已处理
- 趣味评分>7/10
- 无剩余困惑点
Accessibility
无障碍适配
- Keyboard navigation works
- Screen reader compatible
- Color contrast passes
- Reduced motion supported
undefined- 键盘导航正常
- 兼容屏幕阅读器
- 色彩对比度达标
- 支持减少动画模式
undefinedBug Tracking Template
Bug追踪模板
markdown
undefinedmarkdown
undefinedBug Report
Bug报告
Title: [Brief description]
Severity: P0 (Blocker) | P1 (Critical) | P2 (Major) | P3 (Minor)
Environment:
- Build: [version/commit]
- Browser: [name + version]
- Device: [desktop/mobile + specs]
Steps to Reproduce:
1.
2.
3.
Expected Behavior:
[What should happen]
Actual Behavior:
[What actually happens]
Frequency: Always | Sometimes | Rare
Screenshots/Video:
[Attach here]
Additional Context:
[Console errors, network logs, etc.]
undefined标题: [简要描述]
严重程度: P0(阻塞)| P1(关键)| P2(主要)| P3(次要)
环境:
- 构建版本:[版本/提交哈希]
- 浏览器:[名称+版本]
- 设备:[桌面/移动端+配置]
复现步骤:
1.
2.
3.
预期行为:
[应该发生的情况]
实际行为:
[实际发生的情况]
出现频率: 总是 | 有时 | 很少
截图/视频:
[在此处附加]
额外上下文:
[控制台错误、网络日志等]
undefinedReference Documents
参考文档
- Playtesting Methods - Session formats, feedback collection
- Test Automation Guide - Vitest, Playwright patterns
- Code Quality Patterns - Refactoring, clean code
- Performance Profiling - Debugging slowness
- 游戏测试方法 - 测试环节格式、反馈收集
- 自动化测试指南 - Vitest、Playwright使用模式
- 代码质量模式 - 重构、整洁代码
- 性能分析 - 慢代码调试