[C++] 在 unit-testing 中測試 class 的 protected/private 函式

[C++] 在 unit-testing 中測試 class 的 protected/private 函式

最近在用 Google Test 寫 C++ 的 unit-testing 程式,

想當然爾會遇到一些要測試 protected/private function 的問題…

以前我都是會用一個新的 class 來繼承現有的 class,

把原本 protected/private function 變更為 public,

雖然可以動,不過每一個想測的 protected/private function 都得這麼做,真的很累人…

 

舉例來說,下面有個 Scan 類別定義如下:

class Scan
{
public:
Scan();
virtual ~Scan();
protected:
bool Init();
};

 

假設我想測試 Init() 這個 protected 函式,平常是沒辦法的,

得寫一個 child class 來繼承 Scan 類別,藉由 child class 來呼叫 parent class 的 Init() 才行:

class WrapScan : public Scan
{
public:
WrapScan();
virtual ~WrapScan();
bool Init() { return Scan::Init(); }
};
TEST(ScanTest, Init)
{
WrapScan scan;
EXPECT_TRUE(scan.Init());
}

 

今天在網路上查了一下,才發現原來有更好的方法~~

Unit Testing Non Public Member Functions

這篇裡面提到了幾種針對這種問題的解法,像是:

 

1. 用 friend class

但這個我自己也不太喜歡,而且也是會需要為了 unit-testing 而去改原本的程式…

 

2. 繼承原有的 class,改寫成 public

這就是我原本的作法,但不少人不喜歡這種做法,

因為實在太麻煩了,每一個 protected/private function 都得加對應的函式上去…

 

3. 只測試 public function

持這種論點的人認為 public function 是這個類別提供出來的介面,

只需要測試 public function,這樣類別可以把實作的細節隱藏在 protected/private function 裡,

隨時要改實作的方法都沒有問題~

又說如果真的 public function 太過複雜,那可以將 class 切分成更細的 class 互相合作,

各自提供自己的 public function 就可以作測試了~

 

這上面的論點我倒是不太能同意,畢竟 unit-testing 就是要測這個 class 在不同情境下的反應,

假設只測 public function,很有可能因為底層實作的複雜度過高,沒辦法很容易的打出所有情境…

(想像一下如果 public function 下面呼叫了十個 private function,

而每個 private function 裡都有一兩個 if-else,那樣是多少的排列組合?)

而且有時候就是需要去測試 protected/private function,確保它們的運作如我們預期~

 

4. 用 preprocessor 指令將 protected/private 改成 public

這是目前我覺得最好的作法,不用去改到原有的 code,

又可以讓 unit-testing 的程式可以自由的存取 protected/private member。

作法是在 #include class 的宣告檔時,將 protected/private 用 #define 改成 public~

 

以上面的 Scan 類別為例,假設 Scan class 的宣告是在 Scan.h 裡面,

那在 unit-testing 程式在 include Scan.h 前,先用 #define protected public,

將 protected 字改成 public,在 include 結束後改回來 (就不影響之後的 class 宣告),

這樣就能直接測試 Scan 類別的 Init() 函式:

#define protected public
#define private public
#include "Scan.h"
#undef protected
#undef private
TEST(ScanTest, Init)
{
Scan scan;
EXPECT_TRUE(scan.Init());
}

 

目前最後面這個方法用起來相當不錯,不用寫一堆為了 unit-testing 而寫的 wrapper class,

也不用去原有的 class 加上 friend 宣告,也能測到 protected/private function 的細節,

算是相當不錯的技巧囉~^^

 

(本頁面已被瀏覽過 238 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料