gtest是Google的C++測試框架,能夠幫助開發者更簡單快捷得寫好C++單元測試。而且不管你是工做在Linux,Windows仍是Mac上。html
若是你曾寫過單元測試,不管用的什麼語言,相信你很快就能上手。若是還不瞭解,那麼就從這裏開始吧。node
若是你想更深刻了解測試框架,能夠看Kent Beck大神的《測試驅動開發》。linux
gtest_start/ ├─build/ # 構建文件 ├─lib/ # 第三方庫 ├─output/ # 輸出目錄(中間文件,執行文件) │ ├─gtest/ │ └─primer/ │ ├─Debug │ │ └─obj/ │ └─Release ├─src/ # 工程代碼 │ └─primer/ ├─third_party/ # 第三方庫代碼 │ └─gtest/ └─tools/ # 工具腳本
這樣清理方便些。c++
# 準備目錄 mkdir -p gtest_start/third_party/ cd gtest_start/third_party/ # 獲取源碼 svn co http://googletest.googlecode.com/svn/trunk/ gtest
gtest自己提供了多平臺的構建文件,以下:編程
We provide build files for some popular build systems: msvc/ for Visual Studio, xcode/ for Mac Xcode, make/ for GNU make, codegear/ for Borland C++ Builder, and the autotools script (deprecated) and CMakeLists.txt for CMake (recommended) in the Google Test root directory.設計模式
若是你用的不是以上這些,則能夠看make/Makefile或msvc/gtest.sln工程,參考配置。xcode
主要是將gtest-all.cc生成靜態庫。若是帶gtest_main.cc,就省去了寫main函數。框架
Step 1: 建立一個簡單函數:ide
bool IsEven(int num) { return num % 2 == 0; }
Step 2: 用TEST()宏定義並命名一個測試方法:svn
TEST(test_case_name, test_name) { ... test body ... }
對於IsEven()
,能夠這樣:
TEST(IsEvenTest, TestA) { ASSERT_TRUE(IsEven(2)); ASSERT_TRUE(IsEven(3)); }
Step 3: 工程引入gtest_main靜態庫,運行便可。Linux下記得添加-lpthread
。
運行結果
Running main() from gtest_main.cc [==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from IsEvenTest [ RUN ] IsEvenTest.TestA [ OK ] IsEvenTest.TestA (0 ms) [----------] 1 test from IsEvenTest (1 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test case ran. (2 ms total) [ PASSED ] 1 test.
ASSERT_*()宏,當失敗時,會生成致命錯誤,退出當前TEST()。
ASSERT_*()宏相應都會有個EXPECT_*()版本,當失敗時,其生成的是非致命錯誤,會繼續當前TEST()。
例如
TEST(IsEvenTest, TestA) { cout << "IsEvenTest start" << endl; ASSERT_TRUE(IsEven(3)); // failed ASSERT_FALSE(IsEven(3)); cout << "IsEvenTest end" << endl; } TEST(PowerTest, TestA) { cout << "PowerTest start" << endl; EXPECT_EQ(9, Power(2, 3)); // failed, expected: 8 EXPECT_EQ(27, Power(3, 3)); cout << "PowerTest end" << endl; }
運行結果
# ... [ RUN ] IsEvenTest.TestA IsEvenTest start ..\src\primer\simple_unittest.cc(10): error: Value of: IsEven(3) Actual: false Expected: true [ FAILED ] IsEvenTest.TestA (3 ms) # ... [ RUN ] PowerTest.TestA PowerTest start ..\src\primer\simple_unittest.cc(17): error: Value of: Power(2, 3) Actual: 8 Expected: 9 PowerTest end [ FAILED ] PowerTest.TestA (4 ms) # ... [ FAILED ] 2 tests, listed below: # ...
注意:沒有輸出"IsEvenTest end",但有輸出"PowerTest end"。
不過只要是失敗,都會被統計進最後的tests失敗列表。
ASSERT_*()基本的一些宏,請見參考1的Assertions。
當你寫的不少測試都用相似的數據做爲輸入時,這時應當考慮用test fixture。
例如
template<typename T = Data> class Calculator { public: T Plus(T lhs, T rhs) { return lhs + rhs; } T Minus(T lhs, T rhs) { return lhs - rhs; } T Multiplies(T lhs, T rhs) { return lhs * rhs; } T Divides(T lhs, T rhs) { return lhs / rhs; } };
測試Plus、Minus、Multiplies、Divides時,能夠都用兩個一樣的Data做爲數據。這時,你能夠建立一個測試套件:
Step 1: 繼承::testing::Test
。且以protected:
或public:
開始,以使子類能夠訪問。
class CalculatorTest : public ::testing::Test { protected: // ... };
Step 2: 聲明你要用的數據。這兒是Data:
Data* data_a_; Data* data_b_; // 如下爲對ab進行四種操做後的結果: Data plus_ab; Data minus_ab; Data multiplies_ab; Data divides_ab;
Step 3: 在默認構造函數或SetUp()
函數內準備好數據:
CalculatorTest() : plus_ab(10), minus_ab(-6), multiplies_ab(16), divides_ab(0) { } // Sets up the test fixture. virtual void SetUp() { data_a_ = new Data(2); data_b_ = new Data(8); }
Step 4: 在析構函數或TearDown()
函數內釋放數據:
virtual ~CalculatorTest() { } // Tears down the test fixture. virtual void TearDown() { delete data_a_; data_a_ = nullptr; delete data_b_; data_b_ = nullptr; }
Step 5: 用TEST_F()宏來作測試,它容許你訪問套件內成員:
TEST_F(CalculatorTest, Plus) { Calculator<> calculator; EXPECT_EQ(plus_ab, calculator.Plus(*data_a_, *data_b_)); }
運行結果
Running main() from gtest_main.cc [==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from CalculatorTest [ RUN ] CalculatorTest.Plus [ OK ] CalculatorTest.Plus (0 ms) [----------] 1 test from CalculatorTest (1 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test case ran. (4 ms total) [ PASSED ] 1 test.
但這裏,若是同時測試四個,Calculator<> calculator;
豈不是要建立四次。所以咱們能夠考慮將它共享:
Step 1: 定義爲一個static
成員:
class CalculatorTest : public ::testing::Test { protected: // ... // Shared by all tests. static Calculator<>* shared_calculator_; };
固然,別忘記初始化:
Calculator<>* CalculatorTest::shared_calculator_ = nullptr;
Step 2: 在static SetUpTestCase()
函數內準備數據:
// Sets up the stuff shared by all tests in this test case. static void SetUpTestCase() { shared_calculator_ = new Calculator<>; }
Step 3: 在static TearDownTestCase()
函數內釋放數據:
// Tears down the stuff shared by all tests in this test case. static void TearDownTestCase() { delete shared_calculator_; shared_calculator_ = nullptr; }
Step 4: 而後,TEST_F()宏作測試:
TEST_F(CalculatorTest, Plus) { EXPECT_EQ(plus_ab, shared_calculator_->Plus(*data_a_, *data_b_)); }
上述是同一個測試用例下共享數據。另外還有全局環境的,見Global Set-Up and Tear-Down。
本文僅僅是初步體驗了gtest,另外還有死亡測試,事件監聽,以及重複測試、臨時禁用等選項。具體請見參考2。
除此以外,其餘一些有用的文章也列在了參考裏。
以上兩個官方文檔,很是詳細。有必要閱覽一遍,並可做爲手冊。
另外,這個系列也很不錯:玩轉Google開源C++單元測試框架Google Test系列。把官方文檔主要的一些內容都講述了遍。
還有IBM developerWorks 中國上的幾篇:
下載:gtest_start.zip。
build/gtest_start-gcc.cbp # for gnu gcc build/gtest_start-msvc.cbp # for msvc 2010
.cbp由C::B打開便可。
"src/primer/"下爲本文例子,而"src/thoughts/"下是我之前寫的內容:
├─src/ │ ├─primer/ # 本文例子 │ └─thoughts/ │ ├─designpattern/ # C++編程思想設計模式一節例子及其測試 │ ├─gtest/ # gtest原生sample,queue測試例子 │ └─stl/ # stl的測試例子
"designpattern/"下例子,"-Wall"有不少警告,也確實是有些問題的,不用太在乎。
參考
Linux下運行程序時,報出"/usr/lib/libstdc++.so.6: version 'GLIBCXX_3.4.14' not found"的問題。
先前我升級GCC到了4.8.2,且只是軟連接到了高版本的gcc,g++。不知道還要對應升級下libstdc++庫。見:【筆記】CentOS上源碼安裝GCC 4.8.2。
Step 1: 命令檢查libstdc++.so使用的GLIBC版本:
strings /usr/lib/libstdc++.so.6 | grep GLIB
輸出:
GLIBCXX_3.4 GLIBCXX_3.4.1 ... GLIBCXX_3.4.12 GLIBCXX_3.4.13 GLIBC_2.0 ... GLIBC_2.2 GLIBCXX_FORCE_NEW GLIBCXX_DEBUG_MESSAGE_LENGTH
沒發現'GLIBCXX_3.4.14'。
Step 2: 檢查/usr/lib目錄下的libstdc++的庫文件:
ll /usr/lib/libstdc++*
輸出:
lrwxrwxrwx. 1 root root 19 Mar 18 04:57 /usr/lib/libstdc++.so.6 -> libstdc++.so.6.0.13 -rwxr-xr-x. 1 root root 942040 Nov 21 07:28 /usr/lib/libstdc++.so.6.0.13
檢查下安裝:
yum list libstdc++* yum install libstdc++
已是最高版本了。
Step 3: 由GCC版本,搜索了下'libstdc++-4.8.2'。
gcc --version # gcc (GCC) 4.8.2
發現這篇:Installation of Target Libstdc++,看命令源碼目錄下有'libstdc++-v3',確實。
cd /home/join/Env/gcc/gcc-4.8.2/ find ./ -name libstdc++.so*
輸出:
./stage1-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6 ./stage1-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so ./stage1-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.18 ./i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6 ./i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so ./i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.18 ./prev-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6 ./prev-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so ./prev-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.18
我這邊的環境,有三個路徑下有libstdc++.so.6.0.18,libstdc++.so與libstdc++.so.6只是其軟連接。
查看各路徑下libstdc++.so.6.0.18狀態:
stat libstdc++.so.6.0.18
發現各路徑下libstdc++.so.6.0.18都同樣大小。stage1前綴路徑下最先生成,以後prev前綴的,不帶前綴的最晚。除了生成時間,另有個Inode不一樣,存儲在文件系統的索引節點(必然的)。
不清楚Inode,因此瞭解了下:inode,理解inode。
Step 4: 替換老版本libstdc++庫:
# 複製到'/usr/lib/' cd ./i686-pc-linux-gnu/libstdc++-v3/src/.libs/ cp libstdc++.so.6.0.18 cp libstdc++.so.6.0.18 /usr/lib/ # 從新創建軟連接 rm -f libstdc++.so.6 ln -s libstdc++.so.6.0.18 libstdc++.so.6
參考