gtest Google test + lcov coverage 軟體測試 -【C 語言學習筆記】

就業後經常處理定位軟體的測試工作並接觸公司的 QA 同仁,對於習慣學術程式的我,有幸參與 中央大學 鄭永斌 教授的 軟體測試 課程,仍感獲益良多,也更確實了解過去上過 ISO26262, SOTIF 訓練課程中的軟體測試部份。本篇記錄心得及 gtest 及 lcov 的簡易實做。

本篇記錄 gtest 及 lcov 的簡易實做,先簡單記錄課程心得:

所謂的軟體測試工程師,應細分為 Quality Assurance Engineer (QA) & Test Engineer ,前者更著重在軟體 bug 事前的預防、開發流程的規劃及改善,並擬定 test plan ,目標是減少 bug 的發生;而後者則是單純的事後測試,透過 test case 發現 bug ,目標是找出 bug 。其他還有 SDET (Software Development Engineer in Test), QC (Quality Control) 等,這邊就不深入探討。

過去處於網路雲端不發達的時代,軟體一旦出包,就需要透過大量的 CD 光碟或各種磁片等硬件來進行更新,這就有發行量與 license 的成本問題,因此 QA 部門的權力很大,且獨立於 RD (Research and Development) 部門,以起到確實監督的作用。

而目前產業界的發展, QA 或 Tester 作為獨立部門已經漸漸消失,直接由 RD Engineer 承接對應的工作,並附屬在 PM 或 RD 部門內。這是因為現在軟體的 bug 多數能夠用 OTA 方式線上更新,彈性大且更新頻率高,前述的困難與成本不再存在,出現 bug 成為消費者的日常。

因此快速找出 bug 原因,盡快解決變成現今最重要的目標。但由於各項技術的進步,軟體逐漸大型化,動輒數千數萬行的程式碼,使這項任務變得困難。 獨立的 QA 部門難以找出 RD 部門製造的 bug ,軟體容易被更新,且尋找 bug 是 QA 工作的認知,也使得 RD 部門對於 bug 的產生減低了預防意識。故業界目前漸漸走向 RD 自行肩負測試工作的模式。

雖然 QA 不再有獨立部門,但不表示 QA Engineer 會消失。鄭老師認為 QA 職缺雖然萎縮,但會從品質控管與預防的角度,開始擔任更規劃性質的工作。我個人則覺得某種程度上,類似於 ISO 26262 中的 Safety Engineer & Manager ,從工作流程的改進來減少安全危害 (bug) 的發生機率。

因為本人目前也還只是很粗淺的認識,無法針對軟體測試做更深入的討論,諸如軟體測試分類 (動態測試、靜態測試、黑盒測試、白盒測試、功能測試、非功能測試)或其他測試理論,就不在本篇誤人子弟了😅。

接下來就進入軟體測試工具 gtest & lcov 的簡易操作 (Windows 64)。

準備工作

可參考下面連結中的步驟:

https://code.visualstudio.com/docs/cpp/config-mingw

1. 安裝 Visual Studio Code

2. 開啟 Visual Studio Code 安裝外掛 C/C++ extension for VS Code

3. 安裝 MSYS2

4. 開啟 MSYS2 UCRT64 ,使用下面指令安裝 MinGW-w64

pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain

直接按 Enter 使用預設 all 安裝。

5. 加入 MSYS64 到環境變數,本機右鍵 > 內容 > 進階系統設定 > 環境變數 > path 編輯 > 新增 path > 確定。以我的安裝路徑是:

D:\msys64\ucrt64\bin

或在 MSYS2 UCRT64 輸入以下指令:

echo 'PATH=/d/msys64/usr/bin:$PATH' >> ~/.bashrc && source ~/.bashrc

6. 使用下面指令在 MSYS2 UCRT64 確認 g++, g– 及 gdb 正確安裝:

gcc --version
g++ --version
gdb --version

7. 在 sample code 預定路徑,開啟 Windows command prompt (cmd) 輸入指令開啟 Visual Studio Code

code .

接著創建 Hello World app 測試 (新建一個 .cpp file)

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};

    for (const string& word : msg)
    {
        cout << word << " ";
    }
    cout << endl;
}

8. 點擊 Visual Studio Code 介面右上角 ▶ Run C/C++ file ,選擇 g++ complier。

編譯成功看到 Hello World 即完成準備工作。

安裝 gtest & lcov

1. 在 MSYS2 UCRT64 輸入安裝指令:

pacman -S mingw-w64-x86_64-gtest

https://packages.msys2.org/package/mingw-w64-x86_64-gtest

2. 確認下列安裝位置有對應的標頭檔跟函示庫

gtest
gtest

3. 使用下面指令安裝 lcov:

pacman -S lcov

https://packages.msys2.org/package/lcov?repo=msys&variant=x86_64

執行 gtest 和 coverage test

1. 在要測試的代碼內加入 gtest 標頭,下面是一個簡單的加法範例,使用 InitGoogleTest() 初始化 gtest ,並使用 TEST() 撰寫 Test case ,最後以 RUN_ALL_TESTS() 執行測試。

#include <iostream>
#include <vector>
#include <string>
#include <gtest/gtest.h>

using namespace std;

double test_add(const std::vector<double>& nums) {
  if (nums.empty()) {
    throw std::invalid_argument(
        "nums must be provided as non-empty lists.");
  }

  double result = 0;

  for (size_t i = 0; i < nums.size(); ++i) {
    result += nums[i];
  }

  return result;
}

TEST(CalculateAddTest, TestThrow) {
  std::vector<double> nums = {10, 20, 30};
  double expected_result = 60;
  double expected_nums_size = 3;

  double result = test_add(nums);

  ASSERT_EQ(nums.size(), expected_nums_size);
  EXPECT_DOUBLE_EQ(result, expected_result);

}

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

ASSERT_EQ 檢查引數的數目是否符合預期。

EXPECT_DOUBLE_EQ 則是比對計算結果與預期是否相符。

2. 使用下面指令編譯:

g++ helloworld_sw_test.cpp -I /d/msys64/mingw64/include -L /d/msys64/mingw64/lib -lgtest -lgtest_main

執行編譯出的執行黨 a.exe:

./a

但 MSYS terminal 卻沒有顯示 測試 log ,須將編譯指令改為:

g++ helloworld.cpp /d/msys64/mingw64/lib/libgtest.a /d/msys64/mingw64/lib/libgtest_main.a -I /d/msys64/mingw64/include -L /d/msys64/mingw64/lib -lgtest -lgtest_main

可能是環境或路徑參數問題,導致 gtest library 沒有確實被引用,具體原因待查。直接指定編譯 libgtest.a 和 libgtest_main.a 可解決。重新編譯後再執行 a.exe ,結果如下:

故意增加一個引數,使其與預期數量不合,結果如下:

std::vector<double> nums = {10, 20, 30, 40};

故意修改其中一個引數數值,使計算結果不符預期:

std::vector<double> nums = {10, 30, 30};

以上模擬 bug 都是編譯器不會發現的問題,其他測試函數可以參考 Google Test 官網:

https://google.github.io/googletest/reference/assertions.html#exceptions

3. 增加 –coverage 編譯程式以執行 coverage test:

g++ --coverage helloworld_sw_test.cpp /d/msys64/mingw64/lib/libgtest.a /d/msys64/mingw64/lib/libgtest_main.a -I /d/msys64/mingw64/include -L /d/msys64/mingw64/lib -lgtest -lgtest_main

確認目錄下產生 gcno 檔案(跟你的專案名稱有關)。

4. 執行 a.exe ,並確認有 gcda 檔案(跟你的專案名稱有關)。

5. 執行 lcov 並確認產生 coverage.info 檔案。

lcov -c -d . -o coverage.info

6. 使用 genhtml 產生報告。

genhtml coverage.info -o coverage

開啟位在 coverage 資料夾內的 index.htmal。

報告內容:

增加 branch test

1. coverage test 的 lcov 指令加入 –rc lcov_branch_coverage=1 打開 branch option 。

lcov -c -d . -o coverage.info --rc lcov_branch_coverage=1

2. genhtml 指令增加 –branch-coverage 以擷取 branch test 報告。

genhtml --branch-coverage coverage.info -o coverage

報告內容多了 Branch test 欄位:

鄭老師提醒的重要的 solgan

  • 傻言傻語:我是來寫 code 不是來寫 test cases! (品質是所有人的責任)
  • No test cases, no quality !
https://mapostech.com/bashrc/