Google Test:C++14中如何避免重复编写多类同逻辑测试用例
当然有办法!在C++14环境下,你完全可以借助模板元编程、std::index_sequence以及Google Test的底层注册机制,不用手动写15个重复的测试宏,自动生成所有类和字符串组合的测试用例。下面是具体的实现思路和代码示例:
1. 封装通用测试逻辑
首先把所有测试用例的通用逻辑封装到一个模板测试类里,这个类接受两个模板参数:要测试的类类型,以及编译期常量字符串(确保能作为模板参数传递)。
#include <gtest/gtest.h> #include <tuple> #include <utility> #include <string> // 假设这是你要测试的三个类 class ClassA { public: bool process(const std::string& s) { return s.size() > 2; } bool validate(const std::string& s) { return !s.empty(); } }; class ClassB { public: bool process(const std::string& s) { return s.size() < 5; } bool validate(const std::string& s) { return s.find("test") == std::string::npos; } }; class ClassC { public: bool process(const std::string& s) { return s == "str3"; } bool validate(const std::string& s) { return s.size() % 2 == 0; } }; // 编译期常量字符串数组:你的5个测试字符串 constexpr const char* test_strings[] = {"str1", "str2", "str3", "str4", "str5"}; // 模板测试夹具:封装通用测试环境 template <typename TestClass, const char* TestStr> class GenericTestFixture : public ::testing::Test { protected: TestClass test_obj; const std::string target_str = TestStr; }; // 通用测试用例1:测试process方法 template <typename TestClass, const char* TestStr> TEST_F(GenericTestFixture<TestClass, TestStr>, TestProcessMethod) { // 这里可以写通用逻辑,也可以针对不同类做差异化判断(C++14支持constexpr分支) if constexpr (std::is_same<TestClass, ClassA>::value) { ASSERT_TRUE(test_obj.process(target_str)); } else if constexpr (std::is_same<TestClass, ClassB>::value) { ASSERT_TRUE(test_obj.process(target_str)); } else if constexpr (std::is_same<TestClass, ClassC>::value) { ASSERT_EQ(test_obj.process(target_str), (target_str == "str3")); } } // 通用测试用例2:测试validate方法 template <typename TestClass, const char* TestStr> TEST_F(GenericTestFixture<TestClass, TestStr>, TestValidateMethod) { ASSERT_TRUE(test_obj.validate(target_str)); } // 继续添加剩下的3个测试用例...
2. 自动生成所有组合的测试注册
接下来用C++14的std::index_sequence和变参模板,自动遍历所有类和字符串的组合,把每个组合的测试用例注册到Google Test中,避免手动重复注册。
// 测试类的类型列表:把需要测试的类都放在这里 using TestClassList = std::tuple<ClassA, ClassB, ClassC>; namespace detail { // 辅助函数:注册单个类+单个字符串的所有测试用例 template <typename TestClass, const char* TestStr> void register_single_test_combination() { // 注册TestProcessMethod测试 ::testing::RegisterTest( "GenericTests", // 测试套件名称 (std::string("TestProcess_") + typeid(TestClass).name() + "_" + TestStr).c_str(), // 测试用例名称 nullptr, nullptr, __FILE__, __LINE__, // 工厂函数:创建测试夹具实例 []() -> GenericTestFixture<TestClass, TestStr>* { return new GenericTestFixture<TestClass, TestStr>(); } ); // 注册TestValidateMethod测试 ::testing::RegisterTest( "GenericTests", (std::string("TestValidate_") + typeid(TestClass).name() + "_" + TestStr).c_str(), nullptr, nullptr, __FILE__, __LINE__, []() -> GenericTestFixture<TestClass, TestStr>* { return new GenericTestFixture<TestClass, TestStr>(); } ); // 继续注册剩下的3个测试用例... } // 展开字符串索引:为单个类注册所有字符串的测试 template <typename TestClass, std::size_t... StrIndices> void register_class_with_all_strings(std::index_sequence<StrIndices...>) { // 用swallow trick展开所有字符串索引 using swallow = int[]; (void)swallow{0, (register_single_test_combination<TestClass, test_strings[StrIndices]>(), 0)...}; } // 展开类索引:为所有类注册所有字符串的测试 template <std::size_t... ClassIndices> void register_all_combinations(std::index_sequence<ClassIndices...>) { using swallow = int[]; (void)swallow{0, ( register_class_with_all_strings<std::tuple_element_t<ClassIndices, TestClassList>>( std::make_index_sequence<sizeof(test_strings)/sizeof(test_strings[0])>{} ), 0)...}; } } // namespace detail
3. 在main函数中触发注册
最后在main函数里初始化Google Test,并调用我们的辅助函数完成所有测试组合的注册:
int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); // 自动注册3个类 × 5个字符串的所有测试组合 detail::register_all_combinations( std::make_index_sequence<std::tuple_size<TestClassList>::value>{} ); return RUN_ALL_TESTS(); }
补充优化点
- 如果觉得
typeid(TestClass).name()生成的类名不够友好,可以自己写一个类型到名称的映射模板,比如:
然后在注册测试时用template <typename T> struct ClassName; template <> struct ClassName<ClassA> { static constexpr const char* value = "ClassA"; }; template <> struct ClassName<ClassB> { static constexpr const char* value = "ClassB"; }; template <> struct ClassName<ClassC> { static constexpr const char* value = "ClassC"; };ClassName<TestClass>::value代替typeid(TestClass).name()即可。 - 这种方式完全基于C++14模板特性,除了Google Test本身的
TEST_F宏(用来定义通用测试逻辑),没有手动编写15次重复的测试代码,扩展性极强——新增测试类或字符串时,只需要修改TestClassList或test_strings数组即可。
内容的提问来源于stack exchange,提问作者Olav
相关产品推荐
相关产品推荐

