使用Google Test框架测试类Getter时,如何避免频繁修改构造函数参数?
解决Google Test中构造函数扩展导致旧测试失效的问题
这确实是个常见的测试痛点——每次扩展weatherdayRecord的构造函数都要回头修改一堆旧测试,太折腾了。给你几个实用的方案来避免这种情况:
方案1:给构造函数添加默认参数
最简单的办法是修改weatherdayRecord的构造函数,给新增的参数设置合理的默认值。这样旧测试里只传部分参数的代码依然能正常编译,不用修改。
比如把构造函数改成这样:
class weatherdayRecord { public: // 给date参数加默认值,旧代码只传city也能正常工作 weatherdayRecord(std::string city, std::string date = "") { // 原有的验证逻辑 if (!isValidCity(city)) throw std::invalid_argument("Invalid city"); if (!date.empty() && !isValidDate(date)) throw std::invalid_argument("Invalid date"); // ... 初始化成员变量 } // ... Getter方法 };
这样你的旧测试代码完全不用改,新测试需要测试日期时再传第二个参数:
TEST_F(wsRecordTest,DoesItThrowExceptionWhenWrongCitynameIsProvided) { // 旧代码保持不变,依然有效 weatherdayRecord wsRecord{"!#Delft$*"}; ASSERT_THROW(wsRecord.getCity(),std::invalid_argument); } TEST_F(wsRecordTest,DoIGetTheRightDateFromTheConstructor) { // 新测试传完整参数 weatherdayRecord wsRecord{"Delft","2020-10-03"}; ASSERT_EQ(wsRecord.getDate(),"2020-10-03"); }
方案2:使用构建器模式(Builder Pattern)
如果未来还要添加更多字段,默认参数可能会变得杂乱,这时候构建器模式更适合。创建一个weatherdayRecordBuilder类,允许你逐步设置需要的字段,最后生成对象。
示例代码:
class weatherdayRecord { private: // 把构造函数设为私有,强制通过Builder创建 weatherdayRecord(std::string city, std::string date) { // 验证逻辑 if (!isValidCity(city)) throw std::invalid_argument("Invalid city"); if (!isValidDate(date)) throw std::invalid_argument("Invalid date"); // ... 初始化成员 } // Getter方法... public: class Builder { private: std::string city_; std::string date_ = "2020-01-01"; // 默认日期 public: Builder& setCity(std::string city) { city_ = std::move(city); return *this; } Builder& setDate(std::string date) { date_ = std::move(date); return *this; } weatherdayRecord build() { return weatherdayRecord(city_, date_); } }; };
测试时只需要设置当前关心的字段:
TEST_F(wsRecordTest,DoesItThrowExceptionWhenWrongCitynameIsProvided) { // 只设置城市名,日期用默认值 ASSERT_THROW(weatherdayRecord::Builder().setCity("!#Delft$*").build(), std::invalid_argument); } TEST_F(wsRecordTest,DoIGetTheRightDateFromTheConstructor) { auto wsRecord = weatherdayRecord::Builder() .setCity("Delft") .setDate("2020-10-03") .build(); ASSERT_EQ(wsRecord.getDate(),"2020-10-03"); }
这样就算以后添加更多字段(比如温度、湿度),只需要在Builder里加对应的setXXX方法,旧测试完全不用修改。
方案3:在测试夹具中封装实例创建逻辑
利用Google Test的测试夹具(Test Fixture),在wsRecordTest类里封装创建不同实例的方法,避免测试代码直接写构造函数。
示例:
class wsRecordTest : public ::testing::Test { protected: // 创建只设置城市名的实例(日期用默认值) weatherdayRecord createRecordWithCity(const std::string& city) { return weatherdayRecord{city, "2020-01-01"}; // 假设构造函数现在需要两个参数 } // 创建完整参数的实例 weatherdayRecord createFullRecord(const std::string& city, const std::string& date) { return weatherdayRecord{city, date}; } };
测试时调用这些方法:
TEST_F(wsRecordTest,DoesItThrowExceptionWhenWrongCitynameIsProvided) { ASSERT_THROW(createRecordWithCity("!#Delft$*"), std::invalid_argument); } TEST_F(wsRecordTest,DoIGetTheRightDateFromTheConstructor) { auto wsRecord = createFullRecord("Delft","2020-10-03"); ASSERT_EQ(wsRecord.getDate(),"2020-10-03"); }
如果以后构造函数参数变了,只需要修改夹具里的createRecordWithCity和createFullRecord方法,所有测试代码都不用改,维护成本低很多。
内容的提问来源于stack exchange,提问作者Bluewave1976
相关产品推荐
相关产品推荐

