应使用 TEST_F 和 fixture 精准初始化状态变量(如 is_connected、state_),每个测试覆盖单一状态组合,配合 Mock 隔离外部依赖并验证副作用,辅以状态转换表确保路径全覆盖。

用 Google Test 模拟状态切换并验证行为
直接写测试时别急着 new 对象,先想清楚类的状态变量有哪些——比如 is_connected、retry_count、state_ 这类成员。Google Test 的 TEST_F 配合 fixture 能让你在每个测试前精准设置初始状态,比零散的 SetUp() 更可控。
实操建议:
- 把状态字段设为
protected或提供带参数的构造函数 / 初始化方法,避免测试时只能靠 public 接口“绕路”触发状态(比如非要调一次connect()才能进 connected 状态) - 每个测试只覆盖一个状态组合:例如
WhenDisconnected_CallingSend_ReturnsFalse,而不是在一个 test 里反复修改is_connected = true→ 调send()→is_connected = false→ 再调 - 避免在 fixture 的
SetUp()里写复杂逻辑;状态初始化代码应尽量直白,否则一出错你得先 debug 测试本身
手动构造边界状态绕过正常流程
有些状态靠正常调用链根本进不去——比如网络类的 STATE_TIMEOUT_WAITING_ACK,真实环境要等超时,但测试不能等 5 秒。这时候就得“作弊”:用友元类、friend class TestFixture,或把状态枚举和 setter 设为 public(仅限测试编译配置)。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 测试通过但实际运行崩溃:因为手动设了非法状态(如
state_ = STATE_DISCONNECTED但内部缓冲区未清空),结果后续调用访问了悬垂指针 - 测试覆盖了“状态值”,却没验证“该状态下哪些函数该被禁用”:比如
send()在 disconnected 状态下应直接返回,而不是抛异常或静默失败
推荐做法:给类加一个 IsValidState() 辅助函数,在测试中 assert 它,确保你设的状态是类自己认可的合法中间态。
用 Mock 对象隔离外部依赖引发的状态变化
类的状态常由外部事件驱动:比如定时器到期触发重连、socket 回调改变 is_connected。硬等回调或启动真实网络,测试就不可靠。这时要用 MockTimer 或 MockSocket 替换依赖,主动调用 OnTimeout() 或 OnDataReceived() 来推进状态机。
关键点:
- Mock 类必须能触发原类的 protected/private 回调入口(比如把
OnSocketError()设为public,或用friend声明测试 fixture) - 别 mock 整个协议栈——只 mock 直接影响被测类状态的那一层。例如网络类依赖
INetworkInterface,就只 mock 它,不要连底层epoll_wait都 mock - 检查状态变更是否伴随预期副作用:比如从
STATE_CONNECTING切到STATE_CONNECTED时,是否调用了StartKeepaliveTimer()?用 Mock 的EXPECT_CALL验证
状态转换图 + 表格化测试用例防遗漏
手写几十个 TEST_F 很容易漏掉某个跳转路径,比如忘了测 “从 STATE_RETRYING 收到服务器拒绝后是否回到 STATE_IDLE”。先画简化的状态图(顶点=状态,边=事件),再转成二维表:行是当前状态,列是输入事件,单元格填期望的新状态 + 返回值 + 副作用。
这样做能立刻暴露问题:
- 某条边没有对应测试 —— 补一个
TEST_F(ClassNameTest, StateIdle_OnAuthFailed_GoesToStateIdle) - 两个状态之间存在多种跳转条件(比如 timeout vs. network error)—— 拆成两个独立测试,别合并在 if/else 里
- 某个状态没有出边(死状态)—— 是设计缺陷,还是你漏写了处理逻辑?测试会逼你面对它
状态多、转换复杂的类,光靠脑子记不住所有路径。表格不是摆设,是测试清单,每次改状态逻辑,先更新表格再补测试。


















