Google Test Sample07:通过TestWithParam进行接口测试
一、环境信息
- Visual Studio 2019
- Windows 10
- 前导知识:Google Test自带示例六的学习总结
二、Google Test Sample07
1. 示例概述
1.1 示例7讲解了如何通过类testing::TestWithParm测试接口。待测试的接口还是PrimeTable.h(和示例6一样)
class PrimeTable
{
public:
virtual bool IsPrime(int n) const = 0;
virtual int GetNextPrime(int p) const = 0;
virtual ~PrimeTable() {}
};
class OnTheFlyPrimeTable : public PrimeTable{//接口的具体实现};
class PreCalculatedPrimeTable : public PrimeTable{//接口的具体实现};
2. TestWithParm介绍
2.1 类testing::TestWithParm的基础使用如下例所示,共分为三步:
(1) 定义test fixture:test fixture 继承自 类testing::TestWithParam,继承时需要注明参数类型,示例中是整形
(2) 使用宏TEST_P编写具体的测试用例:TEST_P(TestFixtureName,TestCaseName)
(3) 使用INSTANTIATE_TEST_CASE_P(TestSuiteName,TestFixtureName,Values)明确待使用的参数
class Instance2 : public testing::TestWithParam<int> {};
TEST_P(Instance2, basicTest)
{
int n;
n = GetParam();
EXPECT_TRUE(n);
}
INSTANTIATE_TEST_CASE_P(ParamBasicTest, Instance2, testing::Values(1, -1, -2, 2));
2.2 对于GetParam( )函数,它可以定义在test fixture中、也可以定义在TEST_P中。GetParam( )从 ==testing::Values( )==中获取数据,数据类型和test fixture定义时的类型务必一致:示例中是整型
2.3 testing::TestWithParm 与 typed tests ,type-parameterized tests的对比
(1) 都可以用来进行接口测试
(2) typed tests 适用于 接口的实施例已明确的情况,它的使用步骤概括如下:
—首先定义test fixture(继承自类testing::Test 或 其他test fixture)
—其次定义types(即接口的所有实施例);
—然后使用宏 TYPED_TEST_CASE(TestFixtureName,types);明确要使用的test fixture和types
—最后,使用宏TYPED_TEST(TestCaseName, TestName) 编写具体的测试用例。 用例执行的时候,Google Test 会根据TYPED_TEST_CASE中定义的types,重复执行 TYPED_TEST中定义的用例
(3) type-parameterized tests 适用于 接口的实施例不明确的情况(接口仅仅定义,但是不知道具体的实施例),它的使用步骤概括如下:
—首先定义test fixture(继承自类testing::Test 或 其他test fixture)
—其次使用宏 TYPED_TEST_CASE_P(TestFixture) 明确待使用的test fixture
—第三步使用宏TYPED_TEST_P(TestFixture,TestCaseName*)编写测试用例
—第四步通过 REGISTER_TYPED_TEST_SUITE_P(TestFixture,TestCaseName1,TestCaseName2,…) 枚举上面的测试用例
综上, type-parameterized tests 的定义就完成了,它一般会打包为一个头文件,供接口实现者调用。调用方法如下
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> types //定义types
INSTANTIATE_TYPED_TEST_CASE_P(OnTheFlyAndPreCalculated, PrimeTableTest2, types);
3. 对应的单元测试用例
3.1 示例中用到了函数指针,导致代码的阅读难度还是蛮大的
3.2 详细注释请参考下面的代码
3.3 用例执行结果如下
#include "pch.h"
// This sample shows how to test common properties of multiple implementations of an interface (aka interface tests) using value-parameterized tests.
// Each test in the test case has a parameter that is an interface pointer to an implementation tested.
// 示例演示了如何使用 value-parameterized tests 测试一个接口的多个实施例
// 测试用例中的每一条测试都有一个参数,这个参数是一个接口指针、指向待测试的实施例
// The interface and its implementations are in this header.
#include "PrimeTable.h"
namespace
{
using ::testing::TestWithParam;
using ::testing::Values;
// As a general rule, to prevent a test from affecting the tests that come after it,
// you should create and destroy the tested objects for each test instead of reusing them.
// In this sample we will define a simple factory function for PrimeTable objects.
// We will instantiate objects in test's SetUp() method and delete them in TearDown() method.
// 需要避免测试用例之间的互相影响,比如1#用例改变了预置条件中的测试资源,导致2#用例执行失败
// 我们需要保障每一条测试用例所使用的测试资源是一致的,在sample07中,我们通过Test fixture中SetUp()创造测试资源、TearDown()删除测试资源
typedef PrimeTable* CreatePrimeTableFunc(); //将CreatePrimeTableFunc()函数 定义为 返回一个指向PrimeTable类的指针
PrimeTable* CreateOnTheFlyPrimeTable()
{
return new OnTheFlyPrimeTable();
}
template <size_t max> PrimeTable* CreatePreCalculatedPrimeTable()
{
return new PreCalculatedPrimeTable(max);
}
// Inside the test body, fixture constructor, SetUp(), and TearDown() you can refer to the test parameter by GetParam().
// In this case, the test parameter is a factory function which we call in fixture's SetUp() to create and store an instance of PrimeTable.
// 可以通过 GetParam( ) 获取待测试的参数,GetParam( )可以定义在测试用例中,也可以定义在test fixture中的SetUp()和TearDown()
// 在示例中,test parameter是一个factory function(函数地址?),供我们在SetUp( )中创建和存储接口的实施例
class PrimeTableTest : public TestWithParam< CreatePrimeTableFunc*>
{ //CreatePrimeTableFunc*是函数地址(函数地址=函数指针)? 这种使用方法之前没见过
protected:
PrimeTable* table_;
public:
void SetUp() override
{
table_ = ( * GetParam() )(); //GetParam()从Values()中获取数据,本示例获取的是函数地址(函数指针),先加*表示为具体函数、后面跟()表示执行
//Values( &CreateOnTheFlyPrimeTable, &CreatePreCalculatedPrimeTable<1000> ) //函数指针赋值要使用 &
//table_ = (*GetParam())(); 相当于 table_ = CreateOnTheFlyPrimeTable(); + table_ = CreatePreCalculatedPrimeTable<1000>();
}
void TearDown() override
{
delete table_;
table_ = nullptr;
}
~PrimeTableTest() override { delete table_; }
};
TEST_P(PrimeTableTest, ReturnsFalseForNonPrimes)
{
EXPECT_FALSE(table_->IsPrime(-5));
EXPECT_FALSE(table_->IsPrime(0));
EXPECT_FALSE(table_->IsPrime(1));
EXPECT_FALSE(table_->IsPrime(4));
EXPECT_FALSE(table_->IsPrime(6));
EXPECT_FALSE(table_->IsPrime(100));
}
TEST_P(PrimeTableTest, ReturnsTrueForPrimes)
{
EXPECT_TRUE(table_->IsPrime(2));
EXPECT_TRUE(table_->IsPrime(3));
EXPECT_TRUE(table_->IsPrime(5));
EXPECT_TRUE(table_->IsPrime(7));
EXPECT_TRUE(table_->IsPrime(11));
EXPECT_TRUE(table_->IsPrime(131));
}
TEST_P(PrimeTableTest, CanGetNextPrime)
{
EXPECT_EQ(2, table_->GetNextPrime(0));
EXPECT_EQ(3, table_->GetNextPrime(2));
EXPECT_EQ(5, table_->GetNextPrime(3));
EXPECT_EQ(7, table_->GetNextPrime(5));
EXPECT_EQ(11, table_->GetNextPrime(7));
EXPECT_EQ(131, table_->GetNextPrime(128));
}
// In order to run value-parameterized tests, you need to instantiate them,or bind them to a list of values which will be used as test parameters.
// You can instantiate them in a different translation module, or even instantiate them several times.
// 为了运行 value-parameterized tests,你需要实例化他们 或者 将他们绑定至一个列表(这个列表中的值将作为test parameters被使用)。
// 他们=value-parameterized tests
// Here, we instantiate our tests with a list of two PrimeTable object factory functions:
// 下面,通过列表实例化测试(这个列表包含两个PrimeTable对象)
INSTANTIATE_TEST_CASE_P( TestSuitName, PrimeTableTest, Values( &CreateOnTheFlyPrimeTable, &CreatePreCalculatedPrimeTable<1000> ) );
//INSTANTIATE_TEST_SUITE_P(TestCaseName,TestFixtureName,Values). For VS2019, use INSTANTIATE_TEST_CASE_P. //函数指针赋值要使用&
} // namespace