[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 的細節,
算是相當不錯的技巧囉~^^