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* CreatePrimeTable<OnTheFlyPrimeTable>() {
  return new OnTheFlyPrimeTable;
}

template <>
PrimeTable* CreatePrimeTable<PreCalculatedPrimeTable>() {
  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_(CreatePrimeTable<T>()) {}
​
  ~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 Types<OnTheFlyPrimeTable, 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 Types<OnTheFlyPrimeTable, 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 Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> Implementations;

// Types 的定義為:
template <typename T1, typename T2>
struct Types<T1, 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::Types2<T1, 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::TypeList<Implementations>::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 TypeList<Types<T1, 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 Types<T1, 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::Types2<class 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 CaseName<gtest_TypeParam_> {                                   \
   private:                                                                   \
    typedef CaseName<gtest_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 PrimeTableTest<gtest_TypeParam_> {                                   \
   private:                                                                   \
    typedef PrimeTableTest<gtest_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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM