有時候呢,我們就是想直接為 private method 寫測試(任性),可能是因為我們想用測試來知道如何使用 private method,或者用 public method 來測試它實在太難太痛苦啦~
例如一個擁有商業邏輯並且會 call third-party API 的 class 做的事情是:call API 取得一包 XML 資料,parse XML 得到商業邏輯需要的資料,再做商業邏輯上的計算或操作。我們想知道 parse XML 的 private method 是否正確,但它埋在整個流程裡,而用 public method 做整個的 call API、parsing、商業邏輯的測試難以只測試到 parse XML 的部份。
所以,想為 private method 寫測試時該怎麼辦呢?
如果需要測試一個 private method,就該把它設成 public。
看到書上這句話我蠻驚恐的,想著:「等等等,不是吧?就這樣直接把 private method 變成 public 好嗎?這不會在 class 上開出看起來突兀或者不知如何使用的 method 嗎?」
如果不方便將其設為 public,大多數情況下意味著我們的 class 做太多事了,應該進行調整。
喔~原來是這樣~這倒是真的~像上面那個例子,一個 class 既 call third-party API 又 parse XML 又做商業邏輯,太多事情了。
如果我們想測試一個 private method,首先看它是不是個適合在這個 class 當作 public 的 method?如果是,直接改成 public。否則看看是不是這個 class 做太多事了,有些事可以交由另一個 class 處理。例如我們把 parse XML 有關的 method 放到另一個 parser class,這些 method 到了新 class 會變成 public,原本的 class 就能 new 一個 parser 出來做事。
classFlight{ publicfunctionsearch($searchParams) { // call third party api and get response as xml $searchResults = $this->parseSearchResponse($xml); // other impl. }
在 search() 裡先用 http client 向 third party API 送 request 並收 response,response 是一包 XML,我們想 parse 出其中需要的資料而 call parseSearchResponse()。因為 XML 相當複雜,我們希望能單獨確認 parsing 結果是否正確。
這個 class 除了 search、booking、parse 各種 XML 之外還會做許多事情,它的職責太多了,如果我們現在沒有時間去拆解它,卻想測試 parseSearchResponse() 的結果該怎麼做?
首先將 parseSearchResponse() 從 private 變成 protected:
1 2 3 4 5 6 7 8 9 10 11 12
classFlight{ publicfunctionsearch($searchParams) { // call third party api and get response as xml $searchResults = $this->parseSearchResponse($xml); // other impl. }
xUnit framework 這類型 framework 執行測試大致的作法:找到 test class 裡所有 test method(依據語言不同有不同作法,有些語言可以用 reflection),為每個 test method 產生一個單獨的 object,該 object 的任務是去執行那個 test method。利用不同 object 隔離 test case,讓 test case 不會互相影響。