如何在对象构造前调用Google Test的EXPECT_CALL宏
首先纠正你原测试代码里的一个错误:FooTest() : m_foo(MockUI())这种写法是非法的——C++不允许将临时对象绑定到非const的左值引用(Foo构造函数的参数是UI&),编译器会直接报错。必须让MockUI对象拥有和Foo一致的生命周期,比如作为FooTest的成员变量。
针对你核心的问题(Foo构造初始化列表中调用Mock方法,导致期望设置时机尴尬),给你几个可行的解决思路:
方案1:调整成员顺序+Lambda初始化(无侵入,推荐)
利用C++类成员的初始化顺序(严格按照类内声明的顺序执行),把MockUI成员放在Foo前面,再用Lambda表达式在初始化MockUI时提前设置期望:
class FooTest : ::testing::Test { public: // Lambda先执行:创建MockUI并设置调用期望,再初始化m_ui,最后初始化m_foo FooTest() : m_ui([](){ MockUI ui; EXPECT_CALL(ui, get_name()).Times(1); // 指定仅调用1次 return ui; }()), m_foo(m_ui) {} protected: MockUI m_ui; // 必须先声明,确保先于m_foo初始化 Foo m_foo; };
这个方法完全不需要修改生产代码,且每个测试用例会创建独立的MockUI对象,避免用例间的状态干扰。
方案2:给MockUI添加带参数的构造函数(灵活复用)
如果你需要在不同测试场景中设置不同的调用次数期望,可以给MockUI增加一个带参数的构造函数,替代默认构造里固定的期望逻辑:
// mock_ui.hpp 修改后 class MockUI : public UI { public: MockUI() = default; // 默认构造不设置期望,供不需要验证的场景使用 // 传入期望调用次数,-1表示跳过验证 explicit MockUI(int expected_calls) { if (expected_calls >= 0) { EXPECT_CALL(*this, get_name()).Times(expected_calls); } } ~MockUI() = default; MOCK_METHOD(std::string, get_name, (), (override)); };
测试代码里按需初始化:
class FooTest : ::testing::Test { public: // 明确设置get_name仅被调用1次 FooTest() : m_ui(1), m_foo(m_ui) {} protected: MockUI m_ui; Foo m_foo; };
这个方案让MockUI的复用性更强,比如其他测试不需要验证get_name时,直接使用默认构造即可。
方案3:修改Foo类拆分初始化(侵入式,但更符合测试流程)
如果允许调整生产代码的结构,可以把Foo的构造逻辑拆分为默认构造+初始化方法,这样就能在GTest的SetUp阶段先设置Mock期望,再执行初始化:
// foo.cpp 修改后 class Foo { public: Foo() = default; // 空默认构造 // 新增初始化方法,原构造逻辑移到这里 void init(UI& ui) { m_name = ui.get_name(); } ~Foo() = default; private: std::string m_name; };
测试代码:
class FooTest : ::testing::Test { protected: void SetUp() override { EXPECT_CALL(m_ui, get_name()).Times(1); // 先设置期望 m_foo.init(m_ui); // 再执行初始化触发调用 } MockUI m_ui; Foo m_foo; };
这个方案最贴合测试的常规流程,但需要修改生产代码,适合允许调整代码结构的场景。
内容的提问来源于stack exchange,提问作者Foxman
相关产品推荐
相关产品推荐

