sample6

sample6展示了如何测试 使用了同一个接口对相同属性的多个实现——也就是接口测试。
首先编译出sample6,在test/src/samples/CMakeLists.txt最后添加一下两行:

add_executable(sample6_unittest sample6_unittest.cc) 
target_link_libraries(sample6_unittest PUBLIC gtest gtest_main) 

先看一下要测试的接口源码。

prime_tables.h

prime_tables中定义了一个接口PrimeTable,主要有两个方法其一是确定一个数字是否为质数另一是获得一个数的下一个质数。接口定义如下:

class PrimeTable {
public: 
	virtual ~PrimeTable() {} 
	virtual bool IsPrime(int n) const = 0; //判定一个数是否为质数
	virtual int GetNextPrime(int p) const = 0; //获得大于p的质数中邻近p的质数,如果下一个质数超出质数表上限则返回-1.
};

第一种实现方式,实时计算质数OnTheFlyPrimeTable
第二种实现方式,先判断出一个数是否为质数,而后将结果存储到数组中PreCalculatedPrimeTable

sample6_unittest.cc

首先定义了一些工厂函数来创建prime_tables中定义的类的实例,如果你的所有实现都可以用相同的方式创建则可以跳过这一步。

template <class T> 
PrimeTable* CreatePrimeTable(); 

template <> 
PrimeTable* CreatePrimeTable<OnTheFlyPrimeTable>() {
	return new OnTheFlyPrimeTable; 
}

template <>
PrimeTable* CreatePrimeTable<PreClaculatedPrimeTable>() {
	return new PreCalculatedPrimeTable(10000);
}

然后定义一个test fixture模板。

template <class T>
class PrimeTableTest: public testing::Test {
protected:
	PrimeTableTest(): table(CreatePrimeTable<T>()) {} //构造方法使用工厂函数创建一个由T实现的质数表
	~PrimeTableTest() override { delete table_; } 
	PrimeTable* const table_; 
};

using testing::Types;

Google Test提供了两种方式来复用不同类型的测试。第一种叫做typed tests。如果在编写测试的时候已经知道了所有的类型,就应该使用他。

要编写一个typed test case,首先使用TYPED_TEST_SUITE(TestCaseName, TypeList)来声明它并指定类型参数。和TEST_F一样,TestCaseName必须和test fixture名字相同。

typedef Types<OnTheFlyPrimeTable, PreCalculatePrimeTable> Implementations; // 要测试的类型列表。
TYPED_TEST_SUITE(PrimeTableTest, Implementations); 

然后使用 TYPED_TEST(TestCaseName, TestName)来定义一个typed test,和TEST_F类似。

TYPED_TEST(PrimeTableTest, ReturnsFalseForNonPrimes) {
	EXPECT_FALSE(this->table_->IsPrime(-5));
	EXPECT_FALSE(this->table_->IsPrime(0));
	EXPECT_FALSE(this->table_->IsPrime(1));
	EXPECT_FALSE(this->table_->IsPrime(4));
	EXPECT_FALSE(this->table_->IsPrime(6));
	EXPECT_FALSE(this->table_->IsPrime(100));
}

在测试内部,可以用TypeParam来引用类型参数(type parameter),用TestFixture来引用test fixture类,但是以上例子中不需要引用他们。由于使用模板,C++规则要求在引用test fixture类的成员时需要明确写上this->

ps.test内编写的语句没有什么新的内容。

而后是另外两个TYPED_TEST,分别是TYPED_TEST(PrimeTableTest, ReturnsTrueForPrimes)用来测试方法IsPrime对一些质数的返回值是否为True,和TYPED_TEST(PrimeTableTest, CanGetNextPrime)则用来测试方法GetNextPrime对一些整数的返回值是否符合预期。

真相是Google Test会将TYPED_TEST_SUITE中指定的类型列表中的每一个类型进行重复TYPED_TEST测试。
PrimeTableTest的测试输出
从输出结果可以看到,Google Test的确对TYPED_TEST_SUITE中指定的TypeParam进行了重复TYPED_TEST测试。

以上是第一种知道所有要测试的具类类型的测试,接着看另一种不知道所有具类类型的类型测试。

有时在测试的时候并不知道所有要测试的所有类型。例如你是一个接口的设计者,并希望其他人实现它,你可能想写一组测试来确保每一个实现都符合要求,但是不知道将来会写出什么实现方式。
如何在不提交类型参数的情况下编写测试,这就是type-parameterized test可以做的事情,它比typed test复杂一些,但是你可以得到一个在很多情况下可以复用的测试模式,下面是实现方式。

首先定义一个test fixture类模板,这里我们只是重用了之前的PrimeTableTesttest fixture。

template <class T>
class PrimeTableTest2: public PrimeTableTest<T>{};

然后定义test case,参数是test fixture名称,同时也是test case的名字。后缀_P表示参数化或模式(parameterizedorpattern)。

TYPED_TEST_SUITE_P(PrimeTableTest2); 

接着使用TYPED_TEST_P(TestCaseName, TestName)定义一个test,和TEST_F相似。

TYPED_TEST_P(PrimeTableTest2, ReturnsFalseForNonPrimes) {
  EXPECT_FALSE(this->table_->IsPrime(-5));
  EXPECT_FALSE(this->table_->IsPrime(0));
  EXPECT_FALSE(this->table_->IsPrime(1));
  EXPECT_FALSE(this->table_->IsPrime(4));
  EXPECT_FALSE(this->table_->IsPrime(6));
  EXPECT_FALSE(this->table_->IsPrime(100));
}

ok,内容和TYPED_TEST测试一样,之后还有test caseTYPED_TEST_P的另外两个test,TYPED_TEST_P(PrimeTableTest2, ReturnsTrueForPrimes)以及TYPED_TEST_P(PrimeTableTest2, CanGetNextPrime)分别对IsPrime方法和GetNextPrime方法进行测试。

type-parameterized tests涉及一个额外的步骤,必须列举你定义的一个测试。

REGISTER_TYPED_TEST_SUITE_P(PrimeTableTest2, ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime);

第一个参数是test case名称,其余参数是所有test的名称。

这时,test pattern就完成了,然而到现在为之还没有指定想用哪些类型来进行运行测试。
要把抽象的测试类型变成真正的测试,你需要一个类型列表来实例化它。通常测试模式应该定义在一个.h中,任何人都可以包含和实例化它,甚至可以在同一个程序中多次实例化它。为了区分不同的实例你可以给每一个实例取一个名字,这个名字将成为test case名字的一部分,并且可以在test filters中使用。

我们要测试的类型列表,注意他们不一定要在写TYPED_TEST_P()时定义。

typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> PrimeTableImplementations; 
INSTANTIATE_TYPED_TEST_SUITE_P(OnTheFlyAndPreCalculated, PrimeTableTest2, PrimeTableImplementations); 

INSTANTIATE_TYPED_TEST_SUITE_P的三个参数依次为,实例名称(instance name)、test case name、type list。
ok贴一下这部分最后的运行结果:
type-parameterized tests运行结果
可以看到输出的运行结果和type test相比多了一个OnTheFlyAndPreCalculated,是Instance name正如最后那部分描述的那样,instance name会成为test case名字的一部分。

总结一下

最后总结一下,sample6中主要演示了如何对抽象接口的具类实现进行测试,分以下两种测试方式。

已知要测试的抽象接口的实现类的类型

这种情况下可以直接使用type test case,以下是测试步骤。

  • 第一步:定义一个test fixture模板:
template <class T>
class testFixtureName: public testing::Test{}; // 类内部的话则需要利用T来实例化出抽象接口的具体实现类。
  • 第二步:使用TYPED_TEST_SUITE指定要测试的类,TYPED_TEST_SUITE需要连个参数,第一个是上一步定义的test fixture模板类的名字,第二个则是一个Types类型模板,需要指定所有要测试的类名称(Types应该在命名空间testing下)。
typedef Types<className1, className2, className3, ...> Implementations; 
TYPED_TEST_SUITE(testFictureName, Implementations);
  • 第三步:使用TYPED_TEST_SUITE,和定义普通TEST或者TEST_F一样定义test case以及其下的所有test。其中test case的名称需要和第一步中定义的test fixture名称一样。
TYPED_TEST_SUITE(testFixtureName, testName) {}
不知道要测试的所有抽象接口的实现类的类型

这种情况下需要使用type-parameterized test,以下是测试步骤。

  • 第一步:定义test fixture的模板,和上一种情况的定义方式相同。
  • 第二步:使用TYPED_TEST_SUITE_P声明test case,其接受一个参数,参数类型是第一步中定义的test fixture模板类的名字。
TYPED_TEST_SUITE_P(testFixtureName_P);
  • 第三步:使用TYPED_TEST_P定义出具体的test case 和 test,其定义方式和TEST_F相同,两个参数test case name和test name,其中test case name需要和第一步中定义的test fixture名字相同。
TYPED_TEST_P(testFixtureName_P, testName) {} 
  • 第四步:使用REGISTER_TYPE_TEST_SUITE_P列举第三步中定义的所有test,其参数含义为:第一个参数是第一步中定义的test fixture模板名字,其后的参数为第三步中定义的所有test name。
REGISTER_TYPE_TEST_SUITE_P(testFixtureName_P, testName1, testName2, testName3, ...)

以上所有的test-parameterized部分全部定义完毕,最后便是指定要测试的抽象接口的实现类的类型。

  • 最后一步:使用INSTANTIATE_TYPED_TEST_SUITE_P指定要测试的类,三个参数instance name(为每一个实例取的名字)、test case name(和第一步中的test fixture模板类名字相同)、Type list(Types类型,指明了接口实现类的类名字)。更好的做法是将其定义在头文件内,以便更方便被引用。
typedef Types<className1, className2, ...> PrimeTableImplementations;
INSTANTIATE_TYPED_TEST_TUITE_P(InstanceName, testFixtureName, PrimeTableImplementations);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值