为测试修改类方法行为是否合理?MotorControl类测试咨询
现有代码
MotorControl类定义
class MotorControl { private: IAccelStepper *motor; IEncButton2<EB_ENCBTN> *encoder; public: void processEncoder(); void returnToOrigin(); void switchStep(); };
processEncoder方法实现
void MotorControl::processEncoder() { if (encoder->held()) returnToOrigin(); else if (encoder->click()) switchStep(); }
测试需求与疑问
我想要测试processEncoder方法,验证当encoder触发held()或click()时,对应的returnToOrigin()或switchStep()方法是否被调用。我了解Mock技术,但不想Mock整个MotorControl类,所以没法通过编写接口来Mock。GoogleTest提供了Mock非虚方法的方案,但需要给类添加模板。我还考虑过把这些void方法改成返回int类型,让不同分支返回不同值来验证。
想请教:
- 仅为了测试而扩展/修改类的行为(比如加模板、改返回值)属于常规实践吗?
- 如果不Mock方法调用,而是通过控制依赖状态、检查输出结果来验证,但如果方法涉及大量参数,逐一检查是否合理?
解答建议
1. 为测试修改类是否合理?
为测试调整类设计是常规且合理的实践,但要注意修改的必要性和侵入性:
- 用GoogleTest的模板化方案Mock非虚方法,属于低侵入性的测试友好调整,很多项目都会采用。
- 把void方法改成返回状态码,本质是给方法增加可观测性,不仅方便测试,对生产代码的调试也有帮助,只要返回值不破坏原有逻辑,完全可行。
- 更推荐的低侵入方式:把
returnToOrigin()和switchStep()改成protected访问权限,编写测试用子类重写这些方法并记录调用状态:
class TestableMotorControl : public MotorControl { public: int returnToOriginCalled = 0; int switchStepCalled = 0; protected: void returnToOrigin() override { returnToOriginCalled++; } void switchStep() override { switchStepCalled++; } };
这种方式无需修改原有类核心逻辑,就能轻松验证方法调用。
2. 控制依赖状态 vs Mock调用
如果returnToOrigin()和switchStep()有可观测的副作用(比如改变motor状态、修改变量值),那么通过Mockencoder的held()/click()返回值,再检查这些副作用,是更贴近真实场景的黑盒测试,能更好保证代码实际功能正确。
但如果这些方法的副作用难以观测(比如仅发送硬件指令),或者核心验证点就是“方法是否被调用”而非执行结果,Mock方法调用会更高效。
至于大量参数的问题:逐一检查确实繁琐,但可以通过封装断言逻辑、使用参数匹配器(比如GoogleTest的::testing::Args)简化。另外,参数过多也可能提示类设计有优化空间——可以把相关参数封装成结构体,既简化测试,也让生产代码更清晰。
额外提示:利用已有依赖的接口
你的encoder是IEncButton2<EB_ENCBTN>*接口指针,完全可以Mock这个接口来控制held()和click()的返回值,这比MockMotorControl的方法更简单:编写Mock版IEncButton2类,在测试中注入到MotorControl,控制它返回true/false,再验证对应方法是否触发。
内容的提问来源于stack exchange,提问作者MxxCon

