06 | 200个单元测试C项目也能TDD 本文目录 第 6 篇200 个单元测试 —— C 项目也能 TDD 1. 架构C C 混合编译1.1 为什么是 Google Test1.2 构建魔法1.3 为什么 main.c 不能直接测 2. 测试分层2.1 test_game_logic算法完整性2.2 test_fixes回归防护网 3. 精选测试用例3.1 浮点边界NaN / Inf / 1e3003.2 整数边界有符号溢出 UB3.3 RenderTexture 缩放矩阵24 个子测试 4. 测试数的演化 5. 测试的边界 6. 给你的建议 下篇预告最终篇 系列目录系列第 6 篇· 有人说C 写测试太麻烦 —— 200 个用例告诉你C 项目也可以 TDD。上一篇持久化、撤销、提示非核心功能 第 6 篇200 个单元测试 —— C 项目也能 TDD 1. 架构C C 混合编译1.1 为什么是 Google Test方案断言丰富度维护状态Windows 兼容✅Google Test 丰富 活跃 好❌ Unity 基础 一般 好❌ CMocka 一般 缓慢 一般❌ Criterion 一般 一般 一般Google Test 的致命优势参数化测试 丰富的断言宏 活跃社区。1.2 构建魔法g -stdc17 -O2 -Wall \ -I. -Ithird_party/googletest/include \ tests/test_game_logic.cpp \ src/game_logic.c \ ← C 源码直接编译 -L. -lgtest -lgtest_main \ -o tests/test_game_logic.exemain.c含 raylib不可测game_logic.c纯逻辑可测Google TestC 测试框架200 个测试用例全部绿色通过关键game_logic.c直接用g编译。C 编译器对 C 代码兼容性足够好只要代码写得规范。1.3 为什么 main.c 不能直接测main.c链接了 raylib 图形库需要 OpenGL 上下文。在命令行/CI 环境中无法运行。解决方案将PlaceMines、RevealCell、CalculateAdjacent、CheckWin、UpdateStats等不依赖图形的函数提取到game_logic.c。main.c通过#include game_logic.h调用。测试只编译game_logic.c。 2. 测试分层tests/ ├── test_game_logic.cpp # 46 个算法测试 │ # 布雷、展开、和弦、胜利检测 │ └── test_fixes.cpp # 154 个回归测试 # 每个 Fix 对应一组用例 # Fix99: 8 个 ESC 状态矩阵 # Fix108: 24 个缩放比例 # Fix82: 12 个 ValidateStats2.1 test_game_logic算法完整性TEST(RevealCellTest,RevealZeroAdjacentFloodFills){Game g;InitNewGame(g,DIFFICULTY_BEGINNER);// 手动设置棋盘让 (0,0) 为 0 雷格RevealCell(g,0,0);EXPECT_TRUE(g.board[0][0].isRevealed);EXPECT_TRUE(g.board[0][1].isRevealed);// 被泛洪展开EXPECT_TRUE(g.board[1][0].isRevealed);EXPECT_FALSE(g.board[3][3].isRevealed);// 远处的没被展开}2.2 test_fixes回归防护网// Fix #99 ESC 八状态矩阵TEST_F(Fix99EscapeKeyTest,MenuStateExitsGame){game.stateGL_STATE_MENU;EXPECT_EQ(GL_HandleEscape(game),GL_ACTION_QUIT);}TEST_F(Fix99EscapeKeyTest,AllNonMenuStatesReturnMenu){GL_GameState nonMenuStates[]{GL_STATE_PLAYING,GL_STATE_WIN,GL_STATE_LOSE,GL_STATE_STATS,GL_STATE_CUSTOM};for(autos:nonMenuStates){game.states;EXPECT_EQ(GL_HandleEscape(game),GL_ACTION_MENU);}}命名规范Fix{N}{描述}Test。一眼看出这是哪个修复的回归测试。 3. 精选测试用例3.1 浮点边界NaN / Inf / 1e300TEST(ValidateStatsTest,TotalTimeMaxBoundaryReset){GL_Stats stats{};stats.totalTime[0]1e300;// 极端大值GL_ValidateStats(stats);EXPECT_DOUBLE_EQ(stats.totalTime[0],0.0);// 被重置}TEST(ValidateStatsTest,TotalTimeNaNReset){GL_Stats stats{};stats.totalTime[0]nan();// NaNGL_ValidateStats(stats);EXPECT_DOUBLE_EQ(stats.totalTime[0],0.0);} 这些不是万一发生的场景 —— 文件损坏、手动编辑、跨平台复制都会产生异常值。不测就是赌。3.2 整数边界有符号溢出 UBTEST(Fix87IntegerOverflowTest,MaximumBoardSize){unsignedrows16,cols30;intsafeMines(int)((unsigned)rows*(unsigned)cols)-9;EXPECT_EQ(safeMines,471);// 480 - 9}C 标准有符号整数溢出 undefined behavior。用(unsigned)强制转换消除风险。测试验证计算结果正确。3.3 RenderTexture 缩放矩阵24 个子测试TEST_F(Fix108RenderTextureScalingTest,PreciseMatch){UpdateCanvasScale(1280,720);// 精确匹配EXPECT_FLOAT_EQ(canvasScale,1.0f);EXPECT_FLOAT_EQ(canvasOffsetX,0.0f);}TEST_F(Fix108RenderTextureScalingTest,WideScreen){UpdateCanvasScale(1600,720);// 宽屏EXPECT_GT(canvasOffsetX,0.0f);// 水平居中偏移EXPECT_FLOAT_EQ(canvasOffsetY,0.0f);// 垂直无偏移}覆盖 24 种窗口/画布比例组合精确匹配、2 倍缩放、宽屏、窄屏、超宽屏、HD 居中…… 确保canvasScale计算在任何窗口尺寸下都正确。窗口尺寸画布尺寸canvasScalecanvasOffsetXcanvasOffsetY测试结果1280×7201280×7201.00.00.0✅ 精确匹配1600×7201280×7200.5625160.00.0✅ 宽屏960×7201280×7200.56250.00.0✅ 窄屏2560×7201280×7200.5625640.00.0✅ 超宽屏1920×10801280×7201.50.00.0✅ HD 全屏3840×21601280×7203.00.00.0✅ 4K 全屏1280×9601280×7200.750.0120.0✅ 竖屏1280×4801280×7200.66670.00.0✅ 窄高屏 4. 测试数的演化Round 测试数 里程碑 ───── ───── ──────────────────────────── init 0 项目开始 —— 没有测试 ❌ R2-3 46 添加 game_logic 算法测试 R6-13 47 每次 Fix 加一个回归测试 R14 93 浮点/校验和保护测试 R15 100 ESC/坐标/布局回归 R18 146 RenderTexture 缩放矩阵 R24 200 综合功能测试完成200 个用例、全部绿色通过已经成为重构的安全网。 5. 测试的边界单元测试再好也测不了项目原因替代验证图形渲染颜色无 OpenGL 上下文人工目测按钮与文字重叠布局不在 game_logic启动冒烟测试帧时序/动画测试不跑帧循环人工核查文件 I/O 权限无实际文件系统隔离手工测试200 个自动化测试 启动冒烟测试基本能运行 手工 GUI 验证清单才是完整方案。 6. 给你的建议if(项目行数1000){write_tests();// 强烈建议}if(项目有文件 I/O||浮点运算){boundary_tests();// 必须NaN/Inf/INT_MAX 一定会来}if(项目有状态机){state_matrix_tests();// 每个状态 × 每个输入}从第 1 个 Fix 就开始写回归测试。不写的话下次重构你不敢改写了你就有信心。 下篇预告最终篇第 7 篇960×640 → 1280×720一次全局缩放重构实录CELL_SIZE 从 30 到 36 到 40 的迭代决策、菜单/统计/自定义三屏同步布局、HUD 重叠的 5 次修复全过程。 系列目录#标题状态1项目概览与扫雷核心算法✅ 已发布2Raylib 渲染架构✅ 已发布3222 个 Bug 修复教会我的事✅ 已发布4中英文双语的工程实现✅ 已发布5持久化、撤销、提示✅ 已发布6200 个单元测试← 本文✅ 已发布7全局缩放重构实录 待发布如果你觉得有帮助点赞 收藏 关注三连支持作者继续写下去