一、背景 在搜狗内部对于C++项目进行单元测试,基本都是使用gtest进行的。无论是单元测试,还是接口测试我们都做了大量工作。 具体来说,是对函数进行单元测试或接口测试,对模块进行集成测试。但是,对于模板类并没有进行测试,而是对其上层调用进行测试。 现在的问题是,对于模板类需不需要进行单元测试或接口测试?被写成模板类,说明这个类是比较通用且很多地方都在使用它,所以我认为还是有必要进行测试的。 二、如何测试单参数模板类 下面直接进入主题:如何对单参数模板类进行测试? 有如下模板类 // 不用纠结于代码细节,只为说明 template <typename E> class Queue { public: Queue() {} void Enqueue(const E& element) {} E* Dequeue() {} size_t size() const { return (size_t)123; } }; 那么使用gtest该如何对该类进行测试呢? 1、构建工厂类,用于创建被测试类 code // 主模板类 template <class T> Queue<T>* CreateQueue(); // 全特化版本1 template <> Queue<int>* CreateQueue<int>() { return new Queue<int>; } // 全特化版本2 template <> Queue<char>* CreateQueue<char>() { return new Queue<char>; } 2、编写测试类 code template <class T> class QueueTest : public testing::Test { protected: QueueTest() : queue(CreateQueue<T>()) {} virtual ~QueueTest() { delete queue; } Queue<T>* const queue; }; 3、定义需要测试的类型 code typedef testing::Types<int, char> Impls; 4、编写测试用例 code TYPED_TEST_CASE(QueueTest, Impls); TYPED_TEST(QueueTest, DefaultCons) { EXPECT_EQ(123, this->queue->size()); }5、运行用例,结果如下图
温馨提醒 如果使用--gtest_filter进行过滤,则不能写为这样的形式: --gtest_filter=QueueTest.DefaultCons --gtest_filter=QueueTest.* 原因:因为后面带了 /n,n表示数字,需写为如下形式: --gtest_filter=QueueTest* 在前面的例子中,被测试模板类只有一个类型参数,如果是多个类型参数呢,那该如何测试? 两个类型参数的模板类 // 如果有两个类型参数 template <class E, class F> class Que { public: Que() {} void Enque(const E& el) {} E* Deque() {} F size() const { return (F)123; } }; 大家可以静静的想几分钟,看能不能解决这个问题吧~~ 三、如何测试多参数模板类 下面开始讨论这个主题:如何对多参数模板类进行测试? 不能照搬前面方案 当被测试模板类有两个甚至多个类型参数时,再按前面例子的办法来进行行不通了,因为gtest模板类测试宏仅接收一个模板参数,那该如何处理这个问题呢? 我的解决方案 将多个参数放在一个结构体里,做成一个模板类,将该模板类作为一个类型。 将多个参数组合成结构体 template <class A, class B> struct Params { typedef typename A TypeA; typedef typename B TypeB; } 测试框架随之也要改变 template <class T> class TestX : public testing::Test { protected: TestX() : m_i(CreateIndex<typename T::TypeA, typename T::TypeB()) {} virtual ~TestX() { delete m_i; } void Setup() {} void TearDown() {} // Index为被测试模板类 Index<typename T::TypeA, typename T::TypeB>* const m_i; }; 改变需要测试的类型列表的写法 typedef testing::Types<Params<int, int>, Params<char, char> > Impls; 测试用例书写方式也需要更改 TYPED_TEST_CASE(TestX, Impls); TYPED_TEST(TestX, DefaultCons) { // 如果需要使用类型,需要写如下代码 typename TypeParam::TypeA sA; typename TypeParam::TypeB sB; ASSERT_EQ(0, this->m_i->length()); }