執行 Boost Test 的測試:Visual Studio 與 GitLab

| | 0 Comments| 08:49
Categories:

前面簡單介紹了一下 Boost Test 的寫法,接下來來寫一下要跑測試的時候的東西。

首先,基本上如果是獨立撰寫這種測試程式的話,那寫出來的程式就是一般的執行檔,可以直接執行。

但是實際上,由於測試程式通常會越寫越多、所以基本上最後應該是得要有自動化測試的機制才會比較方便。像是 Visual Studio 就有提供測試功能的整合,可以在「測試總管」中直接看到測試的結果;而 GitLab 也可以透過 CI/CD 來產生測試報告、顯示在網頁上。

這邊就拿 Hersey 放在 GitLab 上的 BoostTest(網址)這個專案來當例子吧~

在這個方案裡面,有「Lib」和「Test」兩個資料夾。
其中「Lib」裡面是一個簡單的函式庫,包含一個不完整的二維向量實作;「Test」則就是使用 Boost Test 開發、針對「Lib」所寫的測試。

主要的測試程式檔案則是 Test/Test.cpp連結),裡面有兩個 test case。

而這邊除了有 Visual Studio 2022 的專案檔案之外,也有基本的 Makefile,使用 g++ 來在 Linux 上進行建置。


直接執行時的輸出訊息

而預設執行的時候,基本上他只會顯示錯誤、還有最後彙整的結果,不會有中間的資訊,算是相當簡略。像是這邊的測試程式如果直接執行的話,就只會顯示下面的資訊:

>Test.exe
Running 2 test cases...

*** No errors detected

而如果要顯示警告、或是其他訊息的話,則需要在執行時加上額外的參數;其中最暴力的方法,基本上就是加上「--log_level=all」、讓它顯示所有資訊了~

下面就是這樣執行的結果:

>Test.exe --log_level=all
Running 2 test cases...
Entering test module "Vec2Test"
S:\BoostTest\Test\Test.cpp(15): Entering test suite "Vec2_Test"
S:\BoostTest\Test\Test.cpp(17): Entering test case "constructor"
S:/BoostTest/Test/Test.cpp(20): info: check a.x() == 0.0f has passed
S:/BoostTest/Test/Test.cpp(21): info: check a.y() == 0.0f has passed
S:/BoostTest/Test/Test.cpp(24): info: check b.x() == 1.0f has passed
S:/BoostTest/Test/Test.cpp(25): info: check b.y() == 2.0f has passed
S:/BoostTest/Test/Test.cpp(28): info: check c == b has passed
S:\BoostTest\Test\Test.cpp(17): Leaving test case "constructor"; testing time: 1939us
S:\BoostTest\Test\Test.cpp(31): Entering test case "compute"
S:/BoostTest/Test/Test.cpp(34): info: check a + b == c has passed
S:\BoostTest\Test\Test.cpp(31): Leaving test case "compute"; testing time: 394us
S:\BoostTest\Test\Test.cpp(15): Leaving test suite "Vec2_Test"; testing time: 4158us
Leaving test module "Vec2Test"; testing time: 5087us

*** No errors detected

而這邊的層級也有很多,詳細列表可以參考官方文件,有需要的話可以根據需要來設定。

如果還希望輸出一些額外的資訊,幫助釐清的話,也可以透過 BOOST_TEST_MESSAGE 這個巨集來輸出文字和變數,例如:

int i = 0;
BOOST_TEST_MESSAGE("Test start with i == " << i);

透過 BOOST_TEST_MESSAGE 輸出的訊息層級會是 message,正常執行不會輸出,必須要在執行時加上參數才行。


執行特定測試

如果手邊有一個測試程式,但是跑裡面特定的測試的話,其實也可以透過參數來做控制的。

首先,透過 --list_content 這個參數,可以列出有哪些測試:

>.\Test --list_content
Vec2_Test*
    constructor*
    compute*

而透過 --run_test 則可以指定要執行的測試;例如:

>.\Test --run_test=Vec2_Test/compute --log_level=all
Running 1 test case...
Entering test module "Vec2Test"
S:\BoostTest\Test\Test.cpp(15): Entering test suite "Vec2_Test"
S:\BoostTest\Test\Test.cpp(17): Test case "Vec2_Test/constructor" is skipped because disabled
S:\BoostTest\Test\Test.cpp(31): Entering test case "compute"
S:/BoostTest/Test/Test.cpp(34): info: check a + b == c has passed
S:\BoostTest\Test\Test.cpp(31): Leaving test case "compute"; testing time: 374us
S:\BoostTest\Test\Test.cpp(15): Leaving test suite "Vec2_Test"; testing time: 1736us
Leaving test module "Vec2Test"; testing time: 2759us

*** No errors detected

這邊可以看到,本來兩個 test case 這邊是跳過一個、只執行第二個。

而使用時要注意的,是如果有 test suite 的話,就必須要在 test suite 與 test case 之間加上「/」、以類似路徑的形式來指定;而如果是要執行 test suite 下的所有測試的話,那就只需要指定 test suite 的名字就好了~

如果是要多個特定的測試的話,則也可以透過「,」來連接;而如果是不想執行特定測試的話,也可以透過在測試名稱前加上「!」來排除。

這部分完整的說明,也請自己參考官方文件


Visual Studio 的整合

在 Visual Studio 裡面,本身就有提供 C++ 單元測試的圖形介面整合功能(官網);針對 Boost Test 和 Google Test 的部分,也有提供「測試配接器」(test adapter)來做串接。

不過由於他是獨立的一個可選取的元件,可以讓使用者選擇是否要安裝;所以要使用的話可能要先執行「Visual Studio Installer」來確認自己有沒有安裝這個元件。

而如果都沒問題的話,那在完成專案的建置後,Visual Studio 就會去檢查建置出來的檔案中有沒有可以使用的測試,如果有找到的話,就會顯示在「測試總管」(test explorer)的視窗中。

不過呢…雖然 Boost Test 的元件也是微軟提供的,但是在欄位的對應上,並沒有做得很好。

Visual Studio 裡面的測試架構中間的分層是「命名空間」和「類別」,和 Boost Test 是用 test suite 不相同;而微軟給的 test adapter 也沒有做轉換,所以變成 Boost Test 的 test case 在測試總管裡面,會完全看不出 test suite 的資訊、全部被放到「<空白命名空間> / <空類別>」下,這點算是比較討厭的。

而在這邊,用滑鼠左鍵雙擊就可以跳到對應的程式碼、確認測試的內容;而透過上方的工具列或是右鍵選單、則可以執行個別的測試、或是所有的測試了~最後也會顯示個別測試的結果,以及彙整的結果。

像是下圖就是另外加了一個 test suite 以及錯誤的 test case 的結果:

上面可以看到,有兩個名為「compute」的測試,實際上是屬於不同的 test suite 的,但是由於 Visual Studio 不會去識別 test suite,所以會放在一起、變得有點難以識別。

而如果點選錯誤的測試的話,詳細資料的窗格也會顯示他的錯誤訊息:

不過,這邊都是理想的狀況。實際上 Heresy 在真正使用中的大型專案中,常常會出現 Visual Studio 沒有找到部分測試的狀況;而由於測試總館中的測試基本上都是靠它在背景自動探索冒出來的,所以如果遇到沒有找到想要執行的測試的時候,其實還滿討厭、也不知道該怎麼解決?

另外,理論上從 Visual Studio 2017 開始,就會在程式碼中每個 test case 的位置加上一個圖示、來代表測試的狀態(參考《Announcing CodeLens for C++ Unit Testing》)、同時也可以快速地執行測試;但是在 Heresy 這邊實際使用呢…恩,不能說都沒有出來過,但是看到那個圖示的機會真的少得可憐…這就不知道是怎麼回事了。


整合到 GitLab CI/CD

而如果自己的程式有用 GitLab 來管理的話,其實也可以透過 GitLab CI/CD 的機制、來自動執行測試的~

在這個例子中,.gitlab-ci.yml 的內容可以寫成下面的樣子(網址):

stages:
  - build 
  - test
 
build-job: 
  stage: build 
  image: gcc:13 
  before_script: 
    - apt update; apt install libboost-test-dev -y 
  script: 
    - make 
  artifacts: 
    paths: 
    - TestApp
 
test-job: 
  stage: test 
  image: gcc:13 
  script: 
  - ./TestApp --logger=HRF,message,stdout:JUNIT,all,TestApp.xml 
  artifacts: 
    reports: 
      junit: TestApp.xml

這邊的 pipeline 分成兩個工作,一個是建置用的「build-job」、一個則是測試用的「test-job」;build-job 建置出來的程式「TestApp」會透過 artifact 的機制傳遞給 test-job。

這邊由於是直接拿 GitLab 官方提供的 shared runner 來用,所以都是透過 Linux 的 Docker Executor 來跑、沒辦法跑 Windows 版的;如果需要建置、測試 Windows 版本的話,就會需要自己建立 Windows 的 runner 了(參考一參考二)。

build-job

在 build-job 中,這邊是使用 gcc:13 的 Docker 映像檔,由於有使用 Boost Test,所以在建置前要先透過 apt 安裝 libboost-test-dev 這個套件;之後透過 make 這個指令、就可以透過寫好的 Makefile 來進行建置了~

最後建置出來的測試程式,就是 TestApp 這個檔案;這邊也把他設定成 artifact,除了可以傳遞給 test-job 外,也讓他可以透過 GitLab 的網站系統可以下載。

test-job

測試階段的時候,GitLab 會讓他自動去下載前一個階段的 artifact、也就是 TestApp 這個檔案,所以這邊只要執行程式就好、不需要建置。

不過雖然這邊沒有要編譯程式,但是由於執行 TestApp 的時候會需要 g++13 的 so 檔,所以這邊一樣是使用 gcc:13 這個 Docker 映像檔;相對地,就沒有需要另外安裝 Boost Test 的套件了。

而在執行測試的時候,這邊加上了一個很長的參數「--logger=HRF,message,stdout:JUNIT,all,TestApp.xml」。
--logger」這個參數(官方文件)是用來設定要怎麼輸出 Boost Test 的記錄的,這邊實際上是透過「:」分成兩個部分。

第一段的「HRF,message,stdout」的三個項目,分別代表:

  • HRF:以人類可閱讀的格式(human readable log format)輸出
  • message:輸出層級到「message」為止,效果相當於「--log_level=message
  • stdout:把內容輸出到標準輸出、也就是 console

這部分的目的主要是讓 GitLab 網站上、CI/CD 的工作中可以看到對應的輸出用的(範例)。

第二部分的「JUNIT,all,TestApp.xml」,則是以 JUNIT 的格式、把所有層級的資訊輸出到 TestApp.xml 這個檔案。要這樣設定的原因,是因為 GitLab 支援的測試報告、就是 JUNIT 格式的 XML 檔案(官方文件)。

而最後,則是指定為 JUNIT report 的格式,把產生出來的 XML 檔案透過 artifact 的形式丟給 GitLab Server。

這樣設定好後,如果一切都正確的話,那在 push 到 GitLab 之後,就會透過 GitLab CI/CD 的機制自動建立出一個 pipeline、來進行建置與測試。

而他的 Pipeline 應該會顯示像下面這樣(網頁):

這邊可以看到,Pipeline 右側的「Tests」標示了「2」、代表有兩個測試;而點進去之後,則是會顯示彙整的測試結果。

這邊會是以 Test Job 作為做最上層,然後列出裡面有的測試狀況。

點選「test-job」後,則是會列出裡面的測試項目:

 

可以看到,這邊有正確地處理 Boost Test 裡面的 test suite 名稱;但是可惜的是,沒有使用樹狀結構的形式來做管理。

而透過這樣的設定,就可以讓每次 push 到 GitLab Server 的時候,自動進行測試了~這樣一來,如果程式碼改爛了導致測試執行失敗的時候,就會讓整個 pipeline 被標記成失敗、並通知相關的人了。

如果有要針對多平台、或者多種環境測試的話,也可以自行擴展 .gitlab-ci.yml 的內容來達成;像是 Heresy 這邊基本上就是會自動去建置 Windows 版和 Linux 版、並各自進行測試了~


這篇大概就這樣了?和 Visual Studio 與 GitLab 搭配應用的部分,之後應該會繼續講 code coverage(程式碼覆蓋率)的部分。

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *