unit testing 的第一步:使用 gcov/lcov 統計 c++ project 的 testing coverage

在寫 unit testing 時偶爾會不小心漏掉了一些 case 導致測試不完整,coverage report 可以幫我們做初步的檢查,確保測試程式有一定程度的品質,還可以順便抓出沒有用到的 code

gcc --coverage

為了產生分析 coverage 必要的資料,我們必須在 compiler 時加上 --coverage 的參數,才能夠在程式執行時,得知每行程式碼是否有被跑到,在 link 時也要加上 --coverage。重要的是還要將最佳化關掉,避免最佳化影響統計的結果,可以參考 CMakeLists.txt,我使用的 gcc 版本為 7.4.0。
SET(GCC_COVERAGE_COMPILE_FLAGS "-g -O0 --coverage")
SET(GCC_COVERAGE_LINK_FLAGS "--coverage")
SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
透過這樣的參數來 compile 後,可以發現所有 *.o 的旁邊都多出了 *.gcno 的檔案,可以想成他記錄了每一行的程式碼,之後要用來比對是否有哪一行沒有被執行到

範例

完整的 code 一樣放在 github
這次的範例包含了一個 TestClass 在 libarary 中,裡面有 private function 和會被測試到與不會被測試到的 function 各一個
void TestClass::TestedFunction(bool cond1, bool cond2) const
{
   PrivateFunction(cond1, cond2);
}

void TestClass::UntestedFunction() const
{
   PrivateFunction(false, false);
}

void TestClass::PrivateFunction(bool cond1, bool cond2) const
{
  int local;

  if (cond1) {
    local = 1;
  } else {
    local = 2;
  }

  if (not cond2) {
    local = 3;
  } else {
    local = 4;
  }
}
test.cpp 就是簡單的使用這個 library,會變 compile 成名叫 test-all 的 binary

執行程式並收集結果

在 compile 並執行程式後,會發現所有的 *.o 旁邊又多了一個 *.gcda 檔案,這些檔案就是記錄剛剛的執行有跑過哪些地方,用來和 gcno 檔做對照用的

產生報表

lcov 要用 apt 另外安裝
apt-get install lcov
先用 lcov 指定我們 CMakeFiles 資料夾 (我們的 *.o 檔都在裡面),產生 cov.info 檔,裡面會有分析後的統計資料
lcov -c -d CMakeFiles -o cov.info
再來用與 lcov 一起安裝的另一個程式 genhtml 來產生報表網頁
genhtml cov.info -o report
接著用瀏覽器打開 report 資料夾下的 index.html 就可以看到結合原始碼的結果了

100% coverage 就完美了嗎?

這次的範例可以看到 PrivateFunction 的 coverage 是 100%,但是查看 test.cpp 就可以發現,我們只測試了四種 case 中的其中兩種,所以以 unit testing 的角度來說並不算 cover 了所有測項,但以 coverage 來說已經算是 100% 了,這邊需要特別注意。

留言

這個網誌中的熱門文章

在 python 中透過 ctypes 執行 C++ library 的 class

在 blogger 寫 markdown 最接近完美的辦法