GoogleTest 寫 C++ 單元測試的用法與教學

本篇 ShengYu 介紹 GoogleTest 用法與教學,Google test 又稱為 gtest,gtest 是一個 C++ 的單元測試框架(unit testing framework),gtest 是由 Google 所開發出來的,

以下 Google Test 內容將分為這幾部份,

  • 下載編譯安裝
  • googletest 常用語法
  • googletest 基本的 C++ 單元測試
  • 查詢 googletest 的版本
  • 有在使用 googletest 的開源專案
  • 其它有 test 的開源專案

下載編譯安裝

以下是 GoogleTest 的下載與編譯步驟,

1
2
3
4
5
6
$ git clone https://github.com/google/googletest
$ cd googletest
$ mkdir build
$ cd build
$ cmake -DCMAKE_CXX_FLAGS=-std=c++11 -Dgtest_build_samples=ON ..
$ make -j4

googletest 原始碼目錄下有 googletest 與 googlemock,
注意要切換到 googletest/ 頂層目錄去編譯,不是切換到 googletest/googletest/ 下編譯,否則你可能會遇到 cmake 的錯誤,

1
2
3
4
5
6
7
8
9
CMake Error at CMakeLists.txt:129 (set_target_properties):
set_target_properties called with incorrect number of arguments.


CMake Error at CMakeLists.txt:131 (set_target_properties):
set_target_properties called with incorrect number of arguments.


-- Configuring incomplete, errors occurred!

在 Ubuntu 16.04 下可能會遇到編譯錯誤的訊息,需要加上 std=c++11 的編譯選項,一種方法是在 CMakeLists.txt 裡加入 set(CMAKE_CXX_STANDARD 11)-DCMAKE_CXX_FLAGS=-std=c++11

另外如果電腦是多核心可以下 make -j4 有助於提升編譯速度,

如果要安裝到系統的話,請輸入下列指令,

1
sudo make install

你可以在 /usr/local/include/ 目錄下發現有新的 gtest/gtest.h 標頭檔
/usr/local/include/gtest/gtest.h
你也可以在 /usr/local/lib/ 目錄下發現有新的 libgtest 靜態函式庫
/usr/local/lib/libgtest.a
這些待會都會用到,

googletest 常用語法

這邊列出幾個 googletest 常用斷言,googletest 中定義的斷言如下,

基本斷言
判斷 condition 為真
ASSERT_TRUE(condition);
EXPECT_TRUE(condition);

判斷 condition 為假
ASSERT_FALSE(condition);
EXPECT_FALSE(condition);

數字比較
語法為 ASSERT_EQ(期待值,實際值),期待值和實際值不等時,測試失敗
判斷兩數是否相等,expected == actual
ASSERT_EQ(expected,actual);
EXPECT_EQ(expected,actual);

判斷兩數是否不相等,val1 != val2
ASSERT_NE(val1,val2);
EXPECT_NE(val1,val2);

判斷 val1 是否小於 val2,val1 < val2
ASSERT_LT(val1,val2);
EXPECT_LT(val1,val2);

判斷 val1 是否小於等於 val2,val1 <= val2
ASSERT_LE(val1,val2);
EXPECT_LE(val1,val2);

判斷 val1 是否大於 val2,val1 > val2
ASSERT_GT(val1,val2);
EXPECT_GT(val1,val2);

判斷 val1 是否大於等於 val2,val1 >= val2
ASSERT_GE(val1,val2);
EXPECT_GE(val1,val2);

字元字串比較
判斷兩個 C-style 字元字串有相同的內容
ASSERT_STREQ(expected_str,actual_str);
EXPECT_STREQ(expected_str,actual_str);

判斷兩個 C-style 字元字串有不同的內容
ASSERT_STRNE(str1,str2);
EXPECT_STRNE(str1,str2);

判斷兩個 C-style 字元字串有相同的內容,忽略大小寫
ASSERT_STRCASEEQ(expected_str,actual_str);
EXPECT_STRCASEEQ(expected_str,actual_str);

判斷兩個 C-style 字元字串有不同的內容,忽略大小寫
ASSERT_STRCASENE(str1,str2);
EXPECT_STRCASENE(str1,str2);

googletest 基本的 C++ 單元測試

這邊就示範一下怎麼用 GoogleTest 來寫單元測試,假如我要測試我寫的 myadd 函式的話,

gtest-add.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <gtest/gtest.h>

int myadd(int a, int b) {
return a + b;
}

TEST(testCase, test1) {
EXPECT_EQ(myadd(2, 3), 5);
}

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

接下來用 cmake 編譯或 g++ 編譯都可以,我這邊簡單的用 g++ 作示範,

1
g++ gtest-add.cpp -o a.out -std=c++11 -lgtest -lpthread

或者使用 pkg-config 來輔助,

1
g++ gtest-add.cpp -o a.out -std=c++11 `pkg-config --libs gtest`

執行 a.out 後就可以看到測試的輸出囉~

1
2
3
4
5
6
7
8
9
10
11
$ ./a.out
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from testCase
[ RUN ] testCase.test1
[ OK ] testCase.test1 (0 ms)
[----------] 1 test from testCase (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.

多加幾個測試項目,

gtest-arithmetic.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// g++ gtest-arithmetic.cpp -o a.out -std=c++11 -lgtest -lpthread
#include <gtest/gtest.h>

int myadd(int a, int b) {
return a + b;
}

int mysub(int a, int b) {
return a - b;
}

int mymul(int a, int b) {
return a * b;
}

int mydiv(int a, int b) {
return a / b;
}

TEST(testCase, test1) {
EXPECT_EQ(myadd(2, 3), 5);
}

TEST(testCase, test2) {
EXPECT_EQ(mysub(2, 3), -1);
}

TEST(testCase, test3) {
EXPECT_EQ(mymul(3, 4), 12);
}

TEST(testCase, test4) {
EXPECT_EQ(mydiv(21, 7), 3);
}

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

輸出結果如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ./a.out
[==========] Running 4 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 4 tests from testCase
[ RUN ] testCase.test1
[ OK ] testCase.test1 (0 ms)
[ RUN ] testCase.test2
[ OK ] testCase.test2 (0 ms)
[ RUN ] testCase.test3
[ OK ] testCase.test3 (0 ms)
[ RUN ] testCase.test4
[ OK ] testCase.test4 (0 ms)
[----------] 4 tests from testCase (0 ms total)

[----------] Global test environment tear-down
[==========] 4 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 4 tests.

另外如果 link 連結 gtest_main 的話,就會幫你連結一個 main 主程式入口,那麼你就可以省略不用寫 main 主函式了,

1
g++ gtest-add.cpp -o a.out -std=c++11 -lgtest -lgtest_main -lpthread

那麼程式碼就可以改成這樣,

gtest-arithmetic2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// g++ gtest-arithmetic2.cpp -o a.out -std=c++11 -lgtest -lgtest_main -lpthread
#include <gtest/gtest.h>

int myadd(int a, int b) {
return a + b;
}

int mysub(int a, int b) {
return a - b;
}

int mymul(int a, int b) {
return a * b;
}

int mydiv(int a, int b) {
return a / b;
}

TEST(testCase, test1) {
EXPECT_EQ(myadd(2, 3), 5);
}

TEST(testCase, test2) {
EXPECT_EQ(mysub(2, 3), -1);
}

TEST(testCase, test3) {
EXPECT_EQ(mymul(3, 4), 12);
}

TEST(testCase, test4) {
EXPECT_EQ(mydiv(21, 7), 3);
}

如果是選用 cmake 編譯系統的話 CMakeLists.txt 可以這樣寫,

CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
cmake_minimum_required(VERSION 3.0)

project(gtest-example)

#set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
add_executable(gtest-arithmetic gtest-arithmetic2.cpp)
target_link_libraries(gtest-arithmetic PRIVATE gtest gtest_main pthread)
#set_property(TARGET gtest-arithmetic PROPERTY CXX_STANDARD 11)

enable_testing()
add_test(test_arithmetic1 gtest-arithmetic)

查詢 googletest 的版本

googletest 版本號碼在 CMakeLists.txt 裡會去設定的 GOOGLETEST_VERSION 變數,

1
set(GOOGLETEST_VERSION 1.10.0)

有在使用 googletest 的開源專案

這邊列出比較知名的大型開源專案有在用 googletest,你可以參考學習他們的測試程式碼如何撰寫,有在使用 googletest 的知名開源專案如下,
OpenCV
測試範例:
test_pyramid.cpp
test_grabcut.cpp
test_jpeg.cpp

Kodi(舊名xbmc)
測試範例:
TestNetwork.cpp
TestWebServer.cpp
TestCPUInfo.cpp
Testlog.cpp

gRPC
測試範例:
cmdline_test.cc
stack_tracer_test.cc
mock_stream_test.cc

glog
測試範例:
stl_logging_unittest.cc
logging_unittest.cc

其它有 test 的開源專案

以下是其它有 test 的開源專案,也可以參考看看,
bitcoin
使用 boost test,測試範例:
base64_tests.cpp
crypto_tests.cpp
logging_tests.cpp
sock_tests.cpp

spdlog
使用 Catch2,測試範例:
test_backtrace.cpp
test_misc.cpp

其他參考
C++ 单元测试框架-gtest | Mike’s Blog
https://mikeblog.top/2019/01/02/googletest/
快速上手Google C++ 測試框架googletest_記錄學習的過程-CSDN博客
https://blog.csdn.net/weiwei9363/article/details/103469525
c++单元测试之gtest测试框架快速上手_guotianqing的博客-CSDN博客
https://blog.csdn.net/guotianqing/article/details/104055221
C Unit Test Framework 介紹 (Googletest) | by 亮谷 | Medium
https://medium.com/@ktvexe/c-unit-test-framework-%E4%BB%8B%E7%B4%B9-googletest-9713dadceb7a
輕鬆編寫 C++ 單元測試 | 開源互助社區
https://coctec.com/docs/linux/show-post-68880.html

其它相關文章推薦
C/C++ 新手入門教學懶人包
C/C++ 字串轉數字的4種方法
C++ virtual 的兩種用法
C/C++ 字串反轉 reverse
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別
C++ 類別樣板 class template
std::sort 用法與範例
std::find 用法與範例
std::queue 用法與範例
std::map 用法與範例
std::deque 用法與範例
std::vector 用法與範例