You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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);
}

测试错误情况

  1. 原代码(虚函数)运行错误

------------------------------------------------------------------------------- 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()确实执行了,但框架未检测到该调用。

  1. 改为非虚函数后的错误

------------------------------------------------------------------------------- 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被调用,否则完全不执行。

我有两个问题:

  1. 我的代码示例为何无法正常工作?
  2. 为何必须将被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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 00:40:44