代码质量与工程实践
用 Code Review、Git 规范和自动化测试守住代码质量的底线
前置知识:第01章 CI/CD 流水线 + 基本的编程能力
阅读指南(初学者必看)
为什么你需要学习代码质量实践?
你可能觉得"我的代码能跑就行",但在团队协作中:
- 你改了一行代码,三个功能出了问题——因为你不知道影响范围
- 同事写的代码你不敢改——因为没有测试保护
- 上线后出 Bug,花了 2 小时才定位——因为日志不够、错误没处理
代码质量实践不是"额外工作",而是"节省时间"。 Code Review 防止问题混入,自动化测试确保改代码不出新问题,Git 规范让团队协作不混乱。
学完本章,你能回答:
- Git 分支怎么管?Commit 怎么写才规范?
- Code Review 应该检查什么?游戏项目有什么特殊关注点?
- 单元测试、集成测试、性能测试分别怎么写?
- 怎么用测试覆盖率来衡量代码质量?
- 前端工程化(Webpack/Vite)怎么优化构建?
本文结构
第一部分:Git 规范与分支策略(协作基础)
第二部分:Code Review 最佳实践(团队协作)
第三部分:测试体系(单元/集成/性能测试)
第四部分:前端工程化与构建优化
一、Git 规范与分支策略
分支类型(Git Flow)
| 分支 | 说明 |
|---|---|
| master/main | 线上环境!稳定!只能合入,不能直接提交! |
| develop | 开发环境!最新的开发代码! |
| feature/xxx | 功能分支!从 develop 切!开发完合回 develop! |
| release/xxx | 发布分支!从 develop 切!发布前测试!测完合 master+develop! |
| hotfix/xxx | 紧急修复!从 master 切!修完合 master+develop! |
# 开发新功能
git checkout develop
git checkout -b feature/add-shop
# ... 开发 ...
git checkout develop
git merge feature/add-shop
# 紧急修复
git checkout master
git checkout -b hotfix/fix-login-bug
# ... 修复 ...
git checkout master
git merge hotfix/fix-login-bug
git tag 1.0.1
git checkout develop
git merge hotfix/fix-login-bug
Commit Message 规范(Conventional Commits)
格式:
<type>(<scope>): <subject>
<body>
Type:
| Type | 说明 |
|---|---|
| feat | 新功能 |
| fix | 修复 bug |
| docs | 文档 |
| style | 代码格式(不影响功能) |
| refactor | 重构 |
| test | 测试 |
| chore | 构建/工具链 |
例子:
feat(shop): 新增商店功能
- 商店列表展示
- 购买道具逻辑
fix(login): 修复登录失败问题
- 修复 Token 过期未刷新
模块化开发原则
高内聚:相关的代码放一起!比如商店逻辑放一个模块!
低耦合:模块之间依赖少!改A模块尽量不影响B模块!
H5 游戏目录结构示例:
src/
├─ components/ # 组件
│ ├─ Shop/ # 商店组件
│ ├─ Player/ # 玩家组件
│ └─ ...
├─ scenes/ # 场景
│ ├─ HomeScene/
│ ├─ GameScene/
│ └─ ...
├─ net/ # 网络
│ ├─ ws.ts
│ └─ protocol.ts
├─ store/ # 状态
│ ├─ player.ts
│ └─ shop.ts
└─ utils/ # 工具
├─ math.ts
└─ ...
二、Code Review 最佳实践
Code Review 检查清单:
功能性:
- [ ] 代码是否实现了需求?
- [ ] 边界条件是否处理?
- [ ] 错误处理是否完善?
性能:
- [ ] 是否有不必要的循环?
- [ ] 内存使用是否合理?
- [ ] 数据库查询是否优化?
安全:
- [ ] 用户输入是否验证?
- [ ] SQL 是否参数化?
- [ ] 敏感信息是否脱敏?
可维护性:
- [ ] 命名是否清晰?
- [ ] 函数是否过长(>50行需拆分)?
- [ ] 是否有重复代码?
游戏特殊:
- [ ] 协议是否兼容老版本?
- [ ] 内存泄漏风险?
- [ ] 是否影响帧率?
Code Review 角度与优先级
| 优先级 | 检查维度 | 常见问题 | 游戏特有 |
|---|---|---|---|
| P0 | 正确性 | 逻辑错误、空指针 | 协议不兼容、数值错误 |
| P0 | 安全性 | SQL注入、XSS | 充值漏洞、物品复制 |
| P1 | 性能 | N+1查询、内存泄漏 | 帧率下降、GC卡顿 |
| P1 | 可维护性 | 魔法数字、超长函数 | 状态机混乱、耦合严重 |
| P2 | 风格 | 命名不规范、注释缺失 | 日志格式不统一 |
三、测试体系
单元测试(JUnit + Mockito)
// Java 测试示例
// 单元测试(JUnit + Mockito)
@SpringBootTest
class RoomServiceTest {
@Mock
private RoomRepository roomRepository;
@InjectMocks
private RoomService roomService;
@Test
void createRoom_success() {
// Given
Player player = new Player(1L, "test");
when(roomRepository.save(any())).thenReturn(new Room());
// When
Room room = roomService.createRoom(player, 4);
// Then
assertNotNull(room);
assertEquals(1, room.getPlayers().size());
verify(roomRepository).save(any());
}
@Test
void joinRoom_roomFull_throwsException() {
Room fullRoom = createFullRoom();
when(roomRepository.findById(1L)).thenReturn(Optional.of(fullRoom));
assertThrows(RoomFullException.class, () -> {
roomService.joinRoom(1L, new Player(5L, "new"));
});
}
}
集成测试(Testcontainers)
// 集成测试(Testcontainers)
@Testcontainers
class GameRepositoryIT {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0");
@Test
void saveAndFindPlayer() {
// 使用真实数据库测试
}
}
性能测试(JMH)
// 性能测试(JMH)
@BenchmarkMode(Mode.AverageTime)
@State(Scope.Thread)
class DamageCalculatorBenchmark {
private DamageCalculator calc;
@Setup
void setup() { calc = new DamageCalculator(); }
@Benchmark
long calculateDamage() {
return calc.calculate(100, 50, 1.5);
}
}
测试金字塔
/ E2E测试 \ 少量,验证完整流程
/ 集成测试 \ 适量,验证模块交互
/ 单元测试 \ 大量,验证单个函数
| 测试类型 | 范围 | 速度 | 数量 | 游戏场景 |
|---|---|---|---|---|
| 单元测试 | 单个函数 | 毫秒 | 大量 | 伤害计算、匹配算法 |
| 集成测试 | 模块交互 | 秒级 | 适量 | 数据库操作、服务调用 |
| E2E 测试 | 完整流程 | 分钟 | 少量 | 登录→匹配→战斗→结算 |
| 性能测试 | 性能指标 | 分钟 | 关键路径 | 伤害计算耗时、GC暂停 |
四、前端工程化与构建优化
为什么游戏前端需要工程化?
H5 游戏的前端代码量越来越大:
- 代码需要打包压缩才能上线
- 资源需要按需加载减少首屏时间
- 代码需要 Tree Shaking 移除未使用代码
- SourceMap 需要管理以便线上调试
Webpack/Vite 核心优化
| 优化项 | Webpack 配置 | Vite 配置 | 效果 |
|---|---|---|---|
| 代码分割 | splitChunks |
内置 | 减少首屏加载 |
| Tree Shaking | usedExports |
内置 | 移除死代码 |
| 懒加载 | import() |
import() |
按需加载场景 |
| 压缩 | TerserPlugin |
内置 | 减少体积 |
| SourceMap | devtool |
build.sourcemap |
调试支持 |
游戏前端构建特殊考虑
1. 资源管理
- 图片/音频自动压缩
- 大资源分片加载
- CDN 域名分离
2. 多平台构建
- Web 版:标准 HTML5
- 微信小游戏:适配 wx API
- 抖音小游戏:适配 tt API
3. 版本管理
- 资源文件名加 hash
- 缓存策略配置
- 热更新支持
自问自答
Q:单元测试和集成测试有什么区别? A:单元测试只测一个函数/类,所有外部依赖用 Mock 替代(如 Mock 数据库)。集成测试用真实的依赖(如 Testcontainers 启动真实 MySQL),验证模块之间的交互是否正确。
Q:代码覆盖率多少合适? A:核心逻辑(伤害计算、支付、匹配)争取 80%+,辅助代码(日志、配置)50%+ 即可。追求 100% 覆盖率性价比很低,但关键路径一定要覆盖。
Q:游戏项目有什么特殊的 Code Review 关注点? A:1)协议兼容性——改了协议是否影响老版本客户端;2)内存泄漏——事件监听是否解绑、对象是否回收;3)帧率影响——是否在主循环里做了重操作;4)充值安全——是否有物品复制漏洞。
Q:什么时候写测试,什么时候不写? A:核心逻辑(计算、校验、业务规则)必须写测试,UI 展示和胶水代码可以不写。原则:如果这段代码出 Bug 会造成严重后果(充值错误、数据丢失),就必须写测试。
Q:Git Flow 太复杂,有简单版吗? A:GitHub Flow!只有 main + feature 分支!更简单!适合 CI/CD!10 人以下团队推荐。
Q:怎么防止代码变"屎山"? A:模块化!高内聚低耦合!定期重构!Code Review!写测试!
实践任务
- 任务1:为游戏核心逻辑(伤害计算/匹配算法)编写单元测试,覆盖正常和异常场景
- 任务2:使用 Testcontainers 编写集成测试,验证数据库操作的正确性
- 任务3:配置 SonarQube 代码质量扫描,在 CI/CD 中集成质量门禁
- 任务4:制定团队的 Code Review 规范,编写检查清单模板
- 任务5:用 JMH 为性能关键路径编写基准测试,记录基准数据
- 任务6:优化前端构建配置(代码分割 + Tree Shaking + 懒加载)
与其他章节的关联
| 本章内容 | 关联章节 | 关联点 |
|---|---|---|
| 自动化测试 | 第01章 CI/CD 流水线 | CI 流水线自动运行测试 |
| UI 自动化测试 | 第05章 游戏自动化测试 | 本章的测试是代码级,第05章是 UI 级 |
| 性能测试 | 第06章 游戏性能监控 | 性能测试建立基准,监控发现偏离 |
| 测试与发布 | 第09章 版本管理与灰度发布 | 测试通过是灰度发布的前提 |
| Git 规范 | 第01章 CI/CD 流水线 | 分支策略触发 CI/CD |
上一章:03-Kubernetes编排 | 下一章:05-游戏自动化测试