如何为GTest生成模板参数全组合以简化测试用例扩展
解决GTest中模板类多枚举参数全组合自动生成问题
问题背景
现有枚举类、模板类及GTest测试代码如下:
枚举类定义
enum class first{ a, b }; enum class second{ c, d }; // 可扩展的第三个枚举(示例) enum class third { e, f };
模板类定义
template<first N, second M> class A{ public: A(){} A(int n) { var = n; } int var; }; // 扩展支持三个参数的模板类(示例) template<first N, second M, third K> class A3{ public: A3(){} A3(int n) { var = n; } int var; };
当前测试代码痛点
现有测试用例需要手动枚举所有A的模板参数组合,新增枚举值时需修改MyTypes列表,扩展性差:
template<typename T> class TestA : public ::testing::Test {}; TYPED_TEST_CASE_P(TestA); TYPED_TEST_P(TestA, SomeTest){ TypeParam x(0); EXPECT_EQ(x.var,0); } REGISTER_TYPED_TEST_CASE_P(TestA, SomeTest); // 手动维护组合,扩展性差 typedef ::testing::Types<A<first::a, second::c>, A<first::b, second::d>> MyTypes; INSTANTIATE_TYPED_TEST_CASE_P(My, TestA, MyTypes); int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
需求:自动生成所有枚举参数的笛卡尔积组合,支持2个及以上模板参数,新增枚举值时仅需更新对应参数列表,无需修改测试用例逻辑。
解决方案:模板元编程实现自动组合
通过C++模板元编程实现参数列表的笛卡尔积生成,将每个枚举的所有值封装为类型列表,再组合成A的实例类型列表,最终集成到GTest中。
步骤1:封装枚举值为类型
定义模板类将枚举值转换为类型(GTest的Types仅接受类型参数):
template <auto Val> struct EnumWrapper { static constexpr auto value = Val; // 暴露枚举值 };
步骤2:定义各枚举的全值类型列表
将每个枚举的所有值封装为::testing::Types类型列表,新增枚举值时仅需在此处添加:
// first枚举的全值列表 using FirstEnumList = ::testing::Types< EnumWrapper<first::a>, EnumWrapper<first::b> // 新增first枚举值时直接追加,如EnumWrapper<first::c> >; // second枚举的全值列表 using SecondEnumList = ::testing::Types< EnumWrapper<second::c>, EnumWrapper<second::d> // 新增second枚举值时直接追加 >; // third枚举的全值列表(示例) using ThirdEnumList = ::testing::Types< EnumWrapper<third::e>, EnumWrapper<third::f> >;
步骤3:实现笛卡尔积组合逻辑
二元组合(两个枚举参数)
// 辅助模板:将单个枚举类型与另一枚举列表组合 template <typename SingleEnum, typename EnumList> struct CombineSingleWithList; // 展开列表的特化 template <typename SingleEnum, template <typename...> typename List, typename... Enums> struct CombineSingleWithList<SingleEnum, List<Enums...>> { // 生成SingleEnum与每个Enums组合后的A类型列表 using type = ::testing::Types< A<SingleEnum::value, Enums::value>... >; }; // 辅助模板:两个枚举列表的笛卡尔积 template <typename List1, typename List2> struct CombineTwoLists; // 展开第一个列表的特化 template <template <typename...> typename List, typename... Enums1, typename List2> struct CombineTwoLists<List<Enums1...>, List2> { // 依次将List1中的每个元素与List2组合,再合并为一个大列表 using type = ::testing::Types< typename CombineSingleWithList<Enums1, List2>::type::value_type... >; };
多元组合(三个及以上枚举参数)
基于二元组合扩展,递归实现多列表的笛卡尔积:
// 多元组合递归模板 template <typename... EnumLists> struct CombineMultiLists; // 递归终止条件:单个列表直接返回 template <typename EnumList> struct CombineMultiLists<EnumList> { using type = EnumList; }; // 递归步骤:先组合前n-1个列表,再与第n个列表组合 template <typename FirstList, typename... RestLists> struct CombineMultiLists<FirstList, RestLists...> { using type = typename CombineTwoLists< typename CombineMultiLists<FirstList>::type, typename CombineMultiLists<RestLists...>::type >::type; }; // 针对三参数模板类A3的单元素组合逻辑(示例) template <typename SingleEnum, typename EnumList> struct CombineSingleWithList3; template <typename SingleEnum, template <typename...> typename List, typename... Enums> struct CombineSingleWithList3<SingleEnum, List<Enums...>> { using type = ::testing::Types< A3<SingleEnum::value, Enums::value, typename Enums::value...> >; };
步骤4:集成到GTest测试用例
生成所有组合的类型列表,直接传入INSTANTIATE_TYPED_TEST_CASE_P:
// 生成两个枚举参数的所有组合 using AllTwoParamCombinations = typename CombineTwoLists<FirstEnumList, SecondEnumList>::type; // 生成三个枚举参数的所有组合(示例) using AllThreeParamCombinations = typename CombineMultiLists<FirstEnumList, SecondEnumList, ThirdEnumList>::type; // 实例化双参数测试用例 INSTANTIATE_TYPED_TEST_CASE_P(AllTwoParamTests, TestA, AllTwoParamCombinations); // 针对三参数模板类的测试用例(示例) template<typename T> class TestA3 : public ::testing::Test {}; TYPED_TEST_CASE_P(TestA3); TYPED_TEST_P(TestA3, SomeTest3){ TypeParam x(0); EXPECT_EQ(x.var,0); } REGISTER_TYPED_TEST_CASE_P(TestA3, SomeTest3); INSTANTIATE_TYPED_TEST_CASE_P(AllThreeParamTests, TestA3, AllThreeParamCombinations); int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
扩展说明
- 新增枚举值:仅需在对应枚举的类型列表(如
FirstEnumList)中追加EnumWrapper<新枚举值>,无需修改测试用例逻辑。 - 扩展更多参数:只需新增对应枚举的类型列表,修改
CombineSingleWithList模板以适配新的模板类参数数量,再通过CombineMultiLists组合即可。
内容的提问来源于stack exchange,提问作者三顿饭
相关产品推荐
相关产品推荐

