使用Catch2与TrompeLoeil时Mock调用未被检测的问题咨询
我用Catch2编写测试用例,结合TrompeLoeil做精细化测试,但遇到Mock调用要么完全不执行,要么执行后无法被框架检测到的问题。
最小复现代码
class MyRealClass { public: virtual int returnSumOfValues(int a, int b) { return a + b; } }; class MyMockClass : public MyRealClass { public: MAKE_MOCK2(returnSumOfValues, int(int,int)); }; class MyTestedClass { public: MyTestedClass(MyRealClass* _computeObj) : computeObj(_computeObj) { } int compute() { return computeObj->returnSumOfValues(2,3); } MyRealClass* computeObj; }; TEST_CASE("Testing Trompe L'oeil") { auto mock = new MyMockClass(); MyTestedClass testedClass(mock); int val = testedClass.compute(); CHECK(val == 5); REQUIRE_CALL(*mock, returnSumOfValues(2,3)).RETURN(5); }
测试错误情况
- 原代码(虚函数)运行错误
------------------------------------------------------------------------------- Testing Trompe L'oeil
------------------------------------------------------------------------------- ../../../src/MEGAAutoTests/UnitTests/control/TransferBatchTests.cpp:164
................................................................................./../../src/MEGAAutoTests/UnitTests/main.cpp:38: FAILED: explicitly
with message: No match for call of returnSumOfValues with signature
int(int,int) with.
param _1 == 2
param _2 == 3
调试确认returnSumOfValues()确实执行了,但框架未检测到该调用。
- 改为非虚函数后的错误
------------------------------------------------------------------------------- Testing Trompe L'oeil
------------------------------------------------------------------------------- ../../../src/MEGAAutoTests/UnitTests/control/TransferBatchTests.cpp:164
................................................................................./../../src/MEGAAutoTests/UnitTests/main.cpp:43: FAILED: CHECK(
failure.empty() ) with expansion: false with message: failure :=
"../../../src/MEGAAutoTests/UnitTests/control/TransferBatchTests.
cpp:172 Unfulfilled expectation: Expected
*mock.returnSumOfValues(2,3) to be called once, actually never called
param _1 == 2
param _2 == 3 "
此时Mock函数完全未被调用。
疑问点
根据TrompeLoeil官方文档描述:
The line MAKE_MOCK2(log, void(int severity, const std::string& msg))
creates a mock function void Logger::log(int, const std::string&). If
MAKE_MOCKn(...) or MAKE_CONST_MOCKn(...) are used to implement a
virtual function from a base class, it is always recommended to add a
third macro parameter override since it gives the compiler an ability
to complain about mistakes.
文档说明添加override仅为推荐项,非必须;且未强制要求被Mock的函数为虚函数。但实际测试中必须将函数设为虚函数才能让Mock被调用,否则完全不执行。
我有两个问题:
- 我的代码示例为何无法正常工作?
- 为何必须将被Mock的函数设为虚函数,而文档却暗示这并非强制要求?
1. 代码无法正常工作的原因
核心问题是调用被测函数的顺序早于设置Mock调用期望。
TrompeLoeil的REQUIRE_CALL是预先声明式的——需要在被测代码执行前定义,用来指定“期望Mock函数被以特定参数调用”,而非事后验证已发生的调用。
你的代码执行顺序错误:
- 创建Mock和被测对象
- 调用
testedClass.compute()(此时Mock函数被调用,但未设置任何期望,框架不会记录该调用) - 才设置
REQUIRE_CALL,此时框架检查时,之前的调用已无法被追溯,因此报错“无匹配调用”。
修正后的测试代码需将REQUIRE_CALL放在compute()调用之前:
TEST_CASE("Testing Trompe L'oeil") { auto mock = new MyMockClass(); MyTestedClass testedClass(mock); // 先设置调用期望 REQUIRE_CALL(*mock, returnSumOfValues(2,3)).RETURN(5); int val = testedClass.compute(); CHECK(val == 5); }
另外,建议给MAKE_MOCK2加上override参数,虽非必须,但能让编译器帮你检查是否正确覆盖了基类虚函数:
MAKE_MOCK2(returnSumOfValues, int(int,int), override);
2. 为何必须是虚函数?
TrompeLoeil的Mock类要覆盖基类函数,依赖C++的多态机制——只有基类函数为虚函数时,通过基类指针/引用调用函数才会动态绑定到派生类(Mock类)的实现。
如果基类函数非虚,通过MyRealClass*指针调用returnSumOfValues时,编译器会直接调用基类的原生实现,根本不会走到Mock类的函数里,这是C++基础语法规则,和TrompeLoeil无关。
文档描述的场景是:当直接创建独立Mock类(不继承真实类)时,不需要虚函数——因为你直接用Mock类的对象调用函数,而非通过基类指针。
总结:
- 若Mock类继承自真实类,且通过基类指针调用Mock函数,真实类的函数必须是虚函数,这是C++多态的要求。
- 若创建独立Mock类(无基类),直接用Mock对象调用函数,则不需要虚函数。
内容的提问来源于stack exchange,提问作者Mickaël C. Guimarães

