C++中如何mock std::localtime以适配GTest单元测试需求
方案1:依赖注入改造(优先推荐)
这是最干净、跨平台兼容的方案,不需要依赖编译器特性,还能提升整体代码的可测性。
- 先封装一层时间获取的抽象接口,替代直接调用系统API
class ITimeProvider { public: virtual ~ITimeProvider() = default; virtual time_t getCurrentTime() const = 0; virtual tm* getLocalTime(const time_t* time) const = 0; }; // 生产环境用的默认实现 class SystemTimeProvider : public ITimeProvider { public: time_t getCurrentTime() const override { return time(nullptr); } tm* getLocalTime(const time_t* time) const override { return localtime(time); } };
- 修改原业务代码,将时间提供者作为参数注入,默认用系统实现,不影响原有调用逻辑
假设你原代码在某个函数内,改造后示例:
// 默认参数用系统时间提供者,生产代码调用不需要传参 void yourBusinessFunc(const ITimeProvider& timeProvider = SystemTimeProvider{}) { time_t now = timeProvider.getCurrentTime(); tm *currentTime = timeProvider.getLocalTime(&now); HelperClass helper({(uint8_t)currentTime->tm_mday,(uint8_t)(1+ currentTime->tm_mon), (uint16_t)(currentTime->tm_year+1900), (uint8_t)currentTime->tm_hour, (uint8_t)currentTime->tm_min,(uint8_t)currentTime->tm_sec}); // 原有后续逻辑 }
- 用GMock实现Mock类,编写测试用例
#include <gmock/gmock.h> class MockTimeProvider : public ITimeProvider { public: MOCK_METHOD(time_t, getCurrentTime, (), (const, override)); MOCK_METHOD(tm*, getLocalTime, (const time_t*), (const, override)); }; TEST(HelperClassTest, TimeParamParseCorrect) { MockTimeProvider mockProvider; // 构造测试用的固定时间:2024-05-20 13:14:00 tm testTm = {0}; testTm.tm_mday = 20; testTm.tm_mon = 4; // 系统tm的月份从0开始计数 testTm.tm_year = 2024 - 1900; // 年份从1900开始计数 testTm.tm_hour = 13; testTm.tm_min = 14; testTm.tm_sec = 0; time_t testTime = mktime(&testTm); // 设置mock预期返回值 EXPECT_CALL(mockProvider, getCurrentTime()).WillOnce(testing::Return(testTime)); EXPECT_CALL(mockProvider, getLocalTime(testing::Eq(&testTime))).WillOnce(testing::Return(&testTm)); // 注入mock调用业务逻辑 yourBusinessFunc(mockProvider); // 这里补充你对HelperClass输出结果的断言即可 }
方案2:链接期打桩(适合不能修改原业务代码的场景)
如果你的原代码没法做依赖注入改造,可以用GCC/Clang的链接替换特性打桩系统函数:
- 编译测试程序时加链接参数:
-Wl,--wrap=time,--wrap=localtime - 在测试代码中实现wrap替换函数,用全局变量控制返回值
extern "C" { // 声明真实系统函数 time_t __real_time(time_t* t); tm* __real_localtime(const time_t* t); // 全局mock控制变量,测试前赋值,测试后重置 time_t g_mockTimeVal = 0; tm* g_mockLocalTimeRet = nullptr; time_t __wrap_time(time_t* t) { if (g_mockTimeVal != 0) { if (t) *t = g_mockTimeVal; return g_mockTimeVal; } return __real_time(t); } tm* __wrap_localtime(const time_t* t) { if (g_mockLocalTimeRet != nullptr) { return g_mockLocalTimeRet; } return __real_localtime(t); } }
- 测试用例直接修改全局控制变量即可指定返回值,用完记得重置避免影响其他用例。
内容的提问来源于stack exchange,提问作者Homie
相关产品推荐
相关产品推荐

