testing-team

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Testing Team

测试团队

Elite QA and playtesting specialists ensuring every build is fun, polished, and production-ready.
精英QA与游戏测试专家,确保每个构建版本趣味十足、打磨精良且具备上线条件。

Team Composition

团队构成

RoleDomainFocus
QA LeadTest strategy, release gatesOverall quality ownership
Playtest CoordinatorFun factor, game feelPlayer experience sessions
UX ResearcherUsability, player feedbackData-driven UX decisions
Code Quality EngineerLinting, patterns, refactoringClean, maintainable code
Test Automation EngineerUnit, integration, e2e testsAutomated test coverage
Performance AnalystLoad testing, profiling60fps, fast loads
Accessibility SpecialistWCAG, inclusive designEveryone can play
角色领域核心关注
QA Lead测试策略、发布准入整体质量管控
Playtest Coordinator趣味性、游戏手感玩家体验测试环节
UX Researcher易用性、玩家反馈数据驱动的UX决策
Code Quality Engineer代码检查、代码模式、重构简洁可维护的代码
Test Automation Engineer单元、集成、E2E测试自动化测试覆盖率
Performance Analyst负载测试、性能分析稳定60fps、快速加载
Accessibility SpecialistWCAG、包容性设计全玩家群体适配

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

玩家体验指标

MetricTargetHow to Measure
Session Length>10 minAnalytics
Return Rate>40% D1Analytics
Core Loop Completion>80%Funnel tracking
Rage Quit Rate<5%Session end analysis
Fun Rating>7/10Post-session survey
Confusion Events<3/sessionObserver notes
"Aha!" Moments>2/sessionObserver 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
undefined
markdown
undefined

PR 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
  • 游戏手感得以保留
  • 动画时长符合设计要求
  • 音效触发正确
  • 移动端/触控交互正常
undefined

Refactoring 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+ dependencies
Safe 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
undefined
bash
undefined

Run 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
undefined
bun run check-types
undefined

Performance Testing

性能测试

Metrics & Budgets

指标与预算

MetricBudgetTool
First Contentful Paint<1.5sLighthouse
Time to Interactive<3sLighthouse
Frame Rate60fps stableChrome DevTools
JS Bundle<200KB gzippedBundlesize
Memory (gameplay)<100MBChrome DevTools
Animation Jank0 dropped framesPerformance panel
指标预算工具
首次内容绘制<1.5秒Lighthouse
可交互时间<3秒Lighthouse
帧率稳定60fpsChrome DevTools
JS包体积<200KB(gzip压缩后)Bundlesize
内存占用(游戏中)<100MBChrome DevTools
动画卡顿无丢帧性能面板

Performance Checklist

性能检查清单

markdown
undefined
markdown
undefined

Per-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
  • 滚动无卡顿
  • 电池使用合理
undefined

Build Validation Gates

构建验证准入标准

Pre-Merge Checklist

合并前检查清单

yaml
undefined
yaml
undefined

CI 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
undefined
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
undefined

Release Readiness

发布就绪检查

markdown
undefined
markdown
undefined

Release 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
  • 键盘导航正常
  • 兼容屏幕阅读器
  • 色彩对比度达标
  • 支持减少动画模式
undefined

Bug Tracking Template

Bug追踪模板

markdown
undefined
markdown
undefined

Bug 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.
预期行为: [应该发生的情况]
实际行为: [实际发生的情况]
出现频率: 总是 | 有时 | 很少
截图/视频: [在此处附加]
额外上下文: [控制台错误、网络日志等]
undefined

Reference 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使用模式
  • 代码质量模式 - 重构、整洁代码
  • 性能分析 - 慢代码调试