Sunday, May 29, 2011

Automate Characterization Test by Googletest

也許你我都有面對過自己不熟析的程式碼的經驗,通常我會用兩種方式來了解程式碼:一種方式就是直接面對做 Code Tracing,讀文件,檢視所有的流程;另一種方式就是從外面去測試,Live Debugging,看看這個程式碼的行為為何。

Characterization Test 指的就是類似後者的策略。Characterization Test 主要是要藉由測試的過程,去檢查(Check)現有的程式碼的行為,而不是去驗證(Verify)現有程式碼是否有符合Specification。最終的目的就是要取出可以代表目前程式碼的特徵(Characterizing)。

事實上,很多程式碼本身就沒有 Specification 來定義行為,純粹就是做到剛好可堪用(Just work)。當後來在修改的時候,如果又沒有 Unit Test,要避免任何不必要的 Side-Effect,大概只能夠依靠英雄和好運氣。

還好事情其實沒有那麼糟糕,這時候 Characterization Test 還是可以在悲觀的頹勢中成為我們的安全網。如果收集到的 Test Case 夠多,這時就可以了解到改變會 Break 多少的 Test Case,如果利用這個資訊就可以進而評估影響的範圍。

Characterization Test 可以被實作在各種 Testing Framework 上,對於程式設計師來說,最熟悉的測試工具莫過於 Unit Test Framework。如果是 C/C++ Programmer,Googletest 剛剛好就可以成為自動化 Characterization Test 的好工具。

假設你即將要修正/理解一個函式:(但這個例子好像太簡單了XD)
bool CheckSupportedLocale(std::string locale)
{
    return (0 == locale.compare("en-us"));
}
除了讀程式碼之外,還可以幫它寫一點測試。我現在還不確定結果為何,先隨便給一個 Expected Result:
TEST_F(suite_Locale, CheckSupportedLocale_ENUS)
{
    ASSERT_EQ(false, CheckSupportedLocale("en-us"));
}
執行完之後,Googletest 應該會回報失敗,並且記錄 Actual Result 應該是 true。那麼我們就根據這個結果修正 Test Case。
TEST_F(suite_Locale, CheckSupportedLocale_ENUS_ReturnTrue)
{
    ASSERT_EQ(true, CheckSupportedLocale("en-us"));
}
那麼要怎麼知道自己已經收集足夠多的 Test Case 能夠代表目前程式碼的行為呢?一種簡單的方式就是用 Coverage Tool 來看看 Test Case 目前的 Function/Branch Coverage Rate,比例要多少其實就是要是情況而定了。(大概就是看良心吧XD)

最後,Characterization Test 所找出來的 Test Case,其實就可以在其中篩選出合理的且具有代表性的當做是 Specification,那些 Test Case 最終就可以被轉變成為 Regression Test 中的一環,這樣就可以減輕日後再回頭過來修改程式碼的維護成本了。

延伸閱讀: