Google单元测试框架gtest之官方sample笔记2--类型参数测试

首页 > Google单元测试框架gtest之官方sample笔记2--类型参数测试 > 列表

Google单元测试框架gtest之官方sample笔记2--类型参数测试

gtest 提供了类型参数化测试方案,可以测试不同类型的数据接口,比如模板测试。可以定义参数类型列表,按照列表定义的类型,每个测试case都执行一遍。

本例中,定义了2种计算素数的类,一个是实时计算,一个是提前计算好存放到一个大数组了。既空间和时间实现方式的对比。两种实现类都继承于抽象类PrimeTable。

// The prime table interface.
class PrimeTable {
 public:
  virtual ~PrimeTable() {}
?
  // Returns true if and only if n is a prime number.
  virtual bool IsPrime(int n) const = 0;
?
  // Returns the smallest prime number greater than p; or returns -1
  // if the next prime is beyond the capacity of the table.
  virtual int GetNextPrime(int p) const = 0;
};

为了测试,定义了一个测试类,使用了两种特例化的CreatePrimeTable模板函数。

template class T
PrimeTable* CreatePrimeTable();

template 
PrimeTable* CreatePrimeTableOnTheFlyPrimeTable() {
  return new OnTheFlyPrimeTable;
}

template 
PrimeTable* CreatePrimeTablePreCalculatedPrimeTable() {
  return new PreCalculatedPrimeTable(10000);
}

// Then we define a test fixture class template.
template class T
class PrimeTableTest : public testing::Test {
 protected:
  // The ctor calls the factory function to create a prime table
  // implemented by T.
  PrimeTableTest() : table_(CreatePrimeTableT()) {}
?
  ~PrimeTableTest() override { delete table_; }
?
  // Note that we test an implementation via the base interface
  // instead of the actual implementation class.  This is important
  // for keeping the tests close to the real world scenario, where the
  // implementation is invoked via the base interface.  It avoids
  // got-yas where the implementation class has a method that shadows
  // a method with the same name (but slightly different argument
  // types) in the base interface, for example.
  PrimeTable* const table_;
};

具体测试,有3个测试用例.

TYPED_TEST(PrimeTableTest, ReturnsFalseForNonPrimes) 
TYPED_TEST(PrimeTableTest, ReturnsTrueForPrimes) 
TYPED_TEST(PrimeTableTest, CanGetNextPrime)

sample 6也演示了两种模式,第一种模式是已知所有的类型,先定义为一个Types结构。

typedef TypesOnTheFlyPrimeTable, PreCalculatedPrimeTable Implementations;
#define TYPED_TEST_SUITE TYPED_TEST_CASE
?
TYPED_TEST_SUITE(PrimeTableTest, Implementations);

第二种模式,不知道具体的类型,在运行测试时候,动态的绑定新的类型到测试fixture。

TYPED_TEST_SUITE_P(PrimeTableTest2);
?
REGISTER_TYPED_TEST_SUITE_P(
    PrimeTableTest2,  // The first argument is the test case name.
    // The rest of the arguments are the test names.
    ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime);
?
?
typedef TypesOnTheFlyPrimeTable, PreCalculatedPrimeTable
    PrimeTableImplementations;
?
INSTANTIATE_TYPED_TEST_SUITE_P(OnTheFlyAndPreCalculated,    // Instance name
                               PrimeTableTest2,             // Test case name
                               PrimeTableImplementations);  // Type list

测试结果如下图,PrimeTableTest有2个类型,3个案例。PrimeTableTest2也有2个类型,3个案例。合计运行了12个tests。

代码分析


这个例子非常复杂,分析下主要的函数的调用。

第一步:定义Types

typedef TypesOnTheFlyPrimeTable, PreCalculatedPrimeTable Implementations;

// Types 的定义为:
template typename T1, typename T2
struct TypesT1, T2, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None, internal::None, internal::None, internal::None,
    internal::None {
  typedef internal::Types2T1, T2 type;
};

第二步:定义测试套件(Test suite)

TYPED_TEST_SUITE(PrimeTableTest, Implementations);

//  TYPED_TEST_SUITE 在老版本里叫做TYPED_TEST_CASE, 定义为
# define TYPED_TEST_CASE(CaseName, Types, ...)                             \
  typedef ::testing::internal::TypeList Types ::type GTEST_TYPE_PARAMS_( \
      CaseName);                                                           \
  typedef ::testing::internal::NameGeneratorSelector__VA_ARGS__::type    \
      GTEST_NAME_GENERATOR_(CaseName)

GTEST_TYPE_PARAMS、GTEST_NAME_GENERATOR_、NameGeneratorSelector依赖于:

// GTEST_TYPE_PARAMS
# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
// GTEST_NAME_GENERATOR_
#define GTEST_NAME_GENERATOR_(TestCaseName) \
  gtest_type_params_##TestCaseName##_NameGenerator
?
struct DefaultNameGenerator {
  template typename T
  static std::string GetName(int i) {
    return StreamableToString(i);
  }
};
// NameGeneratorSelector
template typename Provided = DefaultNameGenerator
struct NameGeneratorSelector {
  typedef Provided type;
};

TYPED_TEST_SUITE(PrimeTableTest, Implementations) 用上面的宏替换后:

typedef::testing::internal::TypeListImplementations::type gtest_type_params_PrimeTableTest_; 
typedef ::testing::internal::__VA_ARGS__  gtest_type_params_PrimeTableTest_NameGenerator  
    
//TypeList 扩展
template typename T1, typename T2, typename T3, typename T4, typename T5,
    typename T6, typename T7, typename T8, typename T9, typename T10,
    typename T11, typename T12, typename T13, typename T14, typename T15,
    typename T16, typename T17, typename T18, typename T19, typename T20,
    typename T21, typename T22, typename T23, typename T24, typename T25,
    typename T26, typename T27, typename T28, typename T29, typename T30,
    typename T31, typename T32, typename T33, typename T34, typename T35,
    typename T36, typename T37, typename T38, typename T39, typename T40,
    typename T41, typename T42, typename T43, typename T44, typename T45,
    typename T46, typename T47, typename T48, typename T49, typename T50
struct TypeListTypesT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
    T44, T45, T46, T47, T48, T49, T50  {
  typedef typename TypesT1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
      T41, T42, T43, T44, T45, T46, T47, T48, T49, T50::type type;
};
?
// TypeList的type是 Types::type的别名,2个模板参数的Types::type是Types2类型,所以进一步扩展为:
struct testing::internal::Types2class OnTheFlyPrimeTable,class PreCalculatedPrimeTable gtest_type_params_PrimeTableTest_;
// NameGeneratorSelector__VA_ARGS__::type 类型返回Provided类型参数,默认为DefaultNameGenerator类型
struct testing::internal::DefaultNameGenerator gtest_type_params_PrimeTableTest_NameGenerator
    

第三步:定义测试case,case名字要和test fixture名字相同。

// Then use TYPED_TEST(TestCaseName, TestName) to define a typed test,
// similar to TEST_F.
TYPED_TEST(PrimeTableTest, ReturnsFalseForNonPrimes) {
  // Inside the test body, you can refer to the type parameter by
  // TypeParam, and refer to the fixture class by TestFixture.  We
  // don't need them in this example.
?
  // Since we are in the template world, C++ requires explicitly
  // writing 'this-' when referring to members of the fixture class.
  // This is something you have to learn to live with.
  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));
}
?
TYPED_TEST(PrimeTableTest, ReturnsTrueForPrimes) {
  EXPECT_TRUE(this-table_-IsPrime(2));
  EXPECT_TRUE(this-table_-IsPrime(3));
  EXPECT_TRUE(this-table_-IsPrime(5));
  EXPECT_TRUE(this-table_-IsPrime(7));
  EXPECT_TRUE(this-table_-IsPrime(11));
  EXPECT_TRUE(this-table_-IsPrime(131));
}
?
TYPED_TEST(PrimeTableTest, CanGetNextPrime) {
  EXPECT_EQ(2, this-table_-GetNextPrime(0));
  EXPECT_EQ(3, this-table_-GetNextPrime(2));
  EXPECT_EQ(5, this-table_-GetNextPrime(3));
  EXPECT_EQ(7, this-table_-GetNextPrime(5));
  EXPECT_EQ(11, this-table_-GetNextPrime(7));
  EXPECT_EQ(131, this-table_-GetNextPrime(128));
}

TYPED_TEST是个宏定义,展开为:

# define TYPED_TEST(CaseName, TestName)                                       \
  template typename gtest_TypeParam_                                        \
  class GTEST_TEST_CLASS_NAME_(CaseName, TestName)                            \
      : public CaseNamegtest_TypeParam_ {                                   \
   private:                                                                   \
    typedef CaseNamegtest_TypeParam_ TestFixture;                           \
    typedef gtest_TypeParam_ TypeParam;                                       \
    virtual void TestBody();                                                  \
  };  
?
#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \
  test_case_name##_##test_name##_Test

TYPED_TEST(PrimeTableTest, ReturnsFalseForNonPrimes) 替换掉宏后为:

template typename gtest_TypeParam_                                        \
class PrimeTableTest_ReturnsFalseForNonPrimes_Test                            \
      : public PrimeTableTestgtest_TypeParam_ {                                   \
   private:                                                                   \
    typedef PrimeTableTestgtest_TypeParam_ TestFixture;                           \
    typedef gtest_TypeParam_ TypeParam;                                       \
    virtual void TestBody();                                                  \
  };  

EXPECT_FALSE(this-table_-IsPrime(-5)) 展开如下,使用AssertionResult函数判断是否为true。

// 定义
#define EXPECT_FALSE(condition) \
  GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
                      GTEST_NONFATAL_FAILURE_)
?
#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
  if (const ::testing::AssertionResult gtest_ar_ = \
      ::testing::AssertionResult(expression)) \
    ; \
  else \
    fail(::testing::internal::GetBoolAssertionFailureMessage(\
        gtest_ar_, text, #actual, #expected).c_str())
// 展开为
#define EXPECT_FALSE(condition) \
if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(condition)) ; \
else fail(::testing::internal::GetBoolAssertionFailureMessage(gtest_ar_, #condition, "true", "false").c_str())

具体的函数执行,启动test用例,TestBody()虚函数是入口。

尊重技术文章,转载请注明!

Google单元测试框架gtest之官方sample笔记2--类型参数测试

https://www.cnblogs.com/pingwen/p/14455354.html