gtest使用初级指南
之前在 http://blog.csdn.net/fengbingchun/article/details/39667571?中對google的開源庫gtest進行過介紹,現在看那篇博文,感覺有些沒有說清楚,這里再進行總結下:
Google Test是Google的開源C++單元測試框架,簡稱gtest。它的License是New BSD License,可以商用。它是跨平臺的,不僅可以應用在各PC端(Windows、Linux、Mac),也可以應用在各移動端(Android、iOS)。目前最新的穩定版為1.8.0,可從?https://github.com/google/googletest/releases?下載源碼。
在Windows下編譯gtest source code比較簡單,因為源碼中在googletest/msvc目錄下已經有gtest.sln工程,直接雙擊打開即可。
單元測試(Unit Test)是對軟件基本組成單元(如函數或是一個類的方法)進行的測試,是開發者編寫的一小段代碼,用于檢驗被測試代碼一個很小的、很明確的功能是否正確。
在gtest中,一個測試用例(test case)可以包含一個或多個測試。一個測試程序可以包含多個測試用例。
一、斷言(Assertions)
gtest中,斷言是用以檢查條件是否為真,用以驗證測試code的行為。一個斷言的結果包括成功(success)、非致命失敗(nonfatal failure)、致命失敗(fatal failure)三種。斷言是宏,類似于函數調用。
斷言可以分為兩類,一類是ASSERT_*系列,一類是EXPECT_*系列,它們的區別:EXPECT_*版本的斷言失敗時產生非致命失敗,但不會中止當前函數。ASSERT_*版本的斷言失敗時會產生致命失敗,并結束當前函數,當前函數中ASSERT_*后面的語句將不會執行。
???????? ASSERT_*常常被用于后續測試邏輯強制依賴的處理結果的斷言,如創建對象后檢查指針是否為空,若為空,則后續對象方法調用會失敗;而EXPECT_*則用于即使失敗也不會影響后續測試邏輯的處理結果的斷言,如某個方法返回結果的多個屬性的檢查。
ASSERT_*(expected,actual)/EXPECT_EQ(expected, actual),應該把想要測試的表達式放在actual的位置,把期望值放在expected的位置。
可以通過操作符”<<”將一些自定義的信息輸出,如:
EXPECT_EQ(v1, v2) << “this is a error!”;
斷言包括:
(1)、true/false:ASSERT_TRUE(condition)、ASSERT_FALSE(condition)
(2)、compare two values:ASSERT_EQ(val1, val2)、ASSERT_NE(val1, val2)、ASSERT_LT(val1, val2)、ASSERT_LE(val1, val2)、ASSERT_GT(val1, val2)、ASSERT_GE(val1, val2)
(3)、string comparison: ASSERT_STREQ(str1, str2)、ASSERT_STRNE(str1,str2)、ASSERT_STRCASEEQ(str1, str2)、ASSERT_STRCASENE(str1,str2)
其中帶”CASE”的斷言是忽略大小寫比較。從后綴名就應該可以區分每個比較的意義,如TRUE(條件為真)、FALSE(條件為假)、EQ(=)、NE(!=)、LT(<)、LE(<=)、GT(>)、GE(>=)。以上均存在對應的EXPECT_*斷言。
二、TEST()宏
為了創建一個test:
(1)、使用TEST()來定義和命名一個test函數。TEST()宏是普通的C++函數,沒有返回值。
(2)、在test函數內部,可以包含任何有效地C++語句,并由gtest斷言來檢查值。
(3)、此test函數結果由斷言決定,有任何斷言錯誤(fatally or non-fatally)或在此函數內部產生crash,則此test失敗,否則成功。
TEST()包含兩個參數,從一般到具體。第一個參數表示這個測試用例(test case)的名字,第二個參數表示在這個test case中這個test的名字。名字必須是有效地C++標識符,不能包含下劃線。一個test的全名由test case和其自身名稱組成。
gtest用test case來管理所有test,所以邏輯上相關的test應該放到同一個test case中。
三、TEST_F()宏
如果你寫的多個test都在操作類似的數據,那么你能使用test fixture。它允許你在多個不同的test中復用相同的對象配置。
為了創建一個fixture:
(1)、派生一個類繼承自::testing::Test,并使用protected或public限定符,因為后面將會從子類中訪問fixture的成員。
(2)、在這個派生類里,聲明你想使用的所有對象。
(3)、如果必要,編寫一個默認構造函數或SetUp()函數來準備每個測試對象。
(4)、如果必要,寫一個析構函數或TearDown()函數釋放你在SetUp()中分配的任何資源。
(5)、如果需要,為你的tests定義共享函數。
當用fixture時,用TEST_F()代替TEST(),因為在test fixture中,TEST_F()允許你訪問對象和共享函數。
像TEST(),TEST_F()也有兩個參數,第一個是test case的名字,但是這個名字必須是test fixture類的名字。
在使用TEST_F()之前,你必須先定義一個test fixture類。
對于用TEST_F()定義的每一個test,gtest將會:
(1)、在運行時,創建一個全新的test fixture對象。
(2)、通過SetUp()函數立即初始化它。
(3)、運行test.
(4)、調用TearDown()函數清理申請的資源。
(5)、刪除test fixture對象。在相同的test case中,不同的test有不同的test fixture對象,在創建下一個test fixture對象前,gtest總會先刪除test fixture。在多個test中,gtest不能重用相同的test fixture。在一個test中對test fixture所作的改變不會影響其它的test。
四、testing::InitGoogleTest(argc,argv)函數
testing::InitGoogleTest函數對gtest框架進行初始化,并解析命令行參數,移除所有識別的標志。它允許用戶通過各種標志控制測試程序的行為,如可設置重復運行測試次數、將結果輸出到xml文件。必須在調用RUN_ALL_TESTS之前調用它,它還負責注冊需要運行的所有測試用例。
五、RUN_ALL_TESTS()宏
在定義了所有的test后,通過RUN_ALL_TESTS()執行它們。
RUN_ALL_TESTS():負責執行所有測試,運行所有test,在代碼中只能調用一次,如果所有tests成功,返回0,否則返回1。它會自動地探測并運行你鏈接到的所有測試----它們可以來自不同的測試用例,甚至是來自不同的文件。在默認情況下,結果輸出到標準輸出。
當調用RUN_ALL_TESTS()宏時:
(1)、保存所有gtest標志的狀態。
(2)、為第一個test創建一個test fixture對象。
(3)、通過SetUp()函數初始化。
(4)、在這個fixture對象上運行這個test。
(5)、通過fixture的TearDown()函數釋放資源。
(6)、刪除這個fixture對象。
(7)、恢復所有gtest標志的狀態。
(8)、為下一個test重復上面的操作步驟,直至運行完所有tests。
只能調用RUN_ALL_TESTS()一次。
? ? ? ? 更詳細介紹可參考官網:https://github.com/google/googletest/blob/master/googletest/docs/Primer.md
以下的test code主要來自gtest中的samples:
funset.hpp:
#ifndef FBC_GTEST_TEST_FUNSET_HPP_
#define FBC_GTEST_TEST_FUNSET_HPP_#include <string>// reference: googletest/samples/
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n);
// Returns true iff n is a prime number.
bool IsPrime(int n);///
// A simple string class.
class MyString {
private:const char* c_string_;const MyString& operator=(const MyString& rhs);public:// Clones a 0-terminated C string, allocating memory using new.static const char* CloneCString(const char* a_c_string);// The default c'tor constructs a NULL string.MyString() : c_string_(NULL) {}// Constructs a MyString by cloning a 0-terminated C string.explicit MyString(const char* a_c_string) : c_string_(NULL) {Set(a_c_string);}// Copy c'torMyString(const MyString& string) : c_string_(NULL) {Set(string.c_string_);}// D'tor. MyString is intended to be a final class, so the d'tor// doesn't need to be virtual.~MyString() { delete[] c_string_; }// Gets the 0-terminated C string this MyString object represents.const char* c_string() const { return c_string_; }size_t Length() const {return c_string_ == NULL ? 0 : strlen(c_string_);}// Sets the 0-terminated C string this MyString object represents.void Set(const char* c_string);
};//
// Queue is a simple queue implemented as a singled-linked list.
// The element type must support copy constructor.
template <typename E> // E is the element type
class Queue;// QueueNode is a node in a Queue, which consists of an element of
// type E and a pointer to the next node.
template <typename E> // E is the element type
class QueueNode {friend class Queue<E>;public:// Gets the element in this node.const E& element() const { return element_; }// Gets the next node in the queue.QueueNode* next() { return next_; }const QueueNode* next() const { return next_; }private:// Creates a node with a given element value. The next pointer is set to NULL.explicit QueueNode(const E& an_element) : element_(an_element), next_(NULL) {}// We disable the default assignment operator and copy c'tor.const QueueNode& operator = (const QueueNode&);QueueNode(const QueueNode&);E element_;QueueNode* next_;
};template <typename E> // E is the element type.
class Queue {
public:// Creates an empty queue.Queue() : head_(NULL), last_(NULL), size_(0) {}// D'tor. Clears the queue.~Queue() { Clear(); }// Clears the queue.void Clear() {if (size_ > 0) {// 1. Deletes every node.QueueNode<E>* node = head_;QueueNode<E>* next = node->next();for (;;) {delete node;node = next;if (node == NULL) break;next = node->next();}// 2. Resets the member variables.head_ = last_ = NULL;size_ = 0;}}// Gets the number of elements.size_t Size() const { return size_; }// Gets the first element of the queue, or NULL if the queue is empty.QueueNode<E>* Head() { return head_; }const QueueNode<E>* Head() const { return head_; }// Gets the last element of the queue, or NULL if the queue is empty.QueueNode<E>* Last() { return last_; }const QueueNode<E>* Last() const { return last_; }// Adds an element to the end of the queue. A copy of the element is// created using the copy constructor, and then stored in the queue.// Changes made to the element in the queue doesn't affect the source// object, and vice versa.void Enqueue(const E& element) {QueueNode<E>* new_node = new QueueNode<E>(element);if (size_ == 0) {head_ = last_ = new_node;size_ = 1;}else {last_->next_ = new_node;last_ = new_node;size_++;}}// Removes the head of the queue and returns it. Returns NULL if// the queue is empty.E* Dequeue() {if (size_ == 0) {return NULL;}const QueueNode<E>* const old_head = head_;head_ = head_->next_;size_--;if (size_ == 0) {last_ = NULL;}E* element = new E(old_head->element());delete old_head;return element;}// Applies a function/functor on each element of the queue, and// returns the result in a new queue. The original queue is not// affected.template <typename F>Queue* Map(F function) const {Queue* new_queue = new Queue();for (const QueueNode<E>* node = head_; node != NULL; node = node->next_) {new_queue->Enqueue(function(node->element()));}return new_queue;}private:QueueNode<E>* head_; // The first node of the queue.QueueNode<E>* last_; // The last node of the queue.size_t size_; // The number of elements in the queue.// We disallow copying a queue.Queue(const Queue&);const Queue& operator = (const Queue&);
};#endif // FBC_GTEST_TEST_FUNSET_HPP_
funset.cpp:
#include "funset.hpp"// reference: googletest/samples///
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n) {int result = 1;for (int i = 1; i <= n; i++) {result *= i;}return result;
}// Returns true iff n is a prime number.
bool IsPrime(int n) {// Trivial case 1: small numbersif (n <= 1) return false;// Trivial case 2: even numbersif (n % 2 == 0) return n == 2;// Now, we have that n is odd and n >= 3.// Try to divide n by every odd number i, starting from 3for (int i = 3;; i += 2) {// We only have to try i up to the squre root of nif (i > n / i) break;// Now, we have i <= n/i < n.// If n is divisible by i, n is not prime.if (n % i == 0) return false;}// n has no integer factor in the range (1, n), and thus is prime.return true;
}///
// Clones a 0-terminated C string, allocating memory using new.
const char* MyString::CloneCString(const char* a_c_string) {if (a_c_string == NULL) return NULL;const size_t len = strlen(a_c_string);char* const clone = new char[len + 1];memcpy(clone, a_c_string, len + 1);return clone;
}// Sets the 0-terminated C string this MyString object
// represents.
void MyString::Set(const char* a_c_string) {// Makes sure this works when c_string == c_string_const char* const temp = MyString::CloneCString(a_c_string);delete[] c_string_;c_string_ = temp;
}
test_TEST.cpp:
#include <iostream>
#include <vector>
#include <string>
#include <gtest/gtest.h>
#include "funset.hpp"// reference: googletest/samples/
// Tests factorial of negative numbers.
TEST(FactorialTest, Negative) {// This test is named "Negative", and belongs to the "FactorialTest"// test case.EXPECT_EQ(1, Factorial(-5));EXPECT_EQ(1, Factorial(-1));EXPECT_GT(Factorial(-10), 0);
}// Tests factorial of 0.
TEST(FactorialTest, Zero) {EXPECT_EQ(1, Factorial(0));
}// Tests factorial of positive numbers.
TEST(FactorialTest, Positive) {EXPECT_EQ(1, Factorial(1));EXPECT_EQ(2, Factorial(2));EXPECT_EQ(6, Factorial(3));EXPECT_EQ(40320, Factorial(8));
}// Tests negative input.
TEST(IsPrimeTest, Negative) {// This test belongs to the IsPrimeTest test case.EXPECT_FALSE(IsPrime(-1));EXPECT_FALSE(IsPrime(-2));EXPECT_FALSE(IsPrime(INT_MIN));
}// Tests some trivial cases.
TEST(IsPrimeTest, Trivial) {EXPECT_FALSE(IsPrime(0));EXPECT_FALSE(IsPrime(1));EXPECT_TRUE(IsPrime(2));EXPECT_TRUE(IsPrime(3));
}// Tests positive input.
TEST(IsPrimeTest, Positive) {EXPECT_FALSE(IsPrime(4));EXPECT_TRUE(IsPrime(5));EXPECT_FALSE(IsPrime(6));EXPECT_TRUE(IsPrime(23));
}/
// Tests the default c'tor.
TEST(MyString, DefaultConstructor) {const MyString s;EXPECT_STREQ(NULL, s.c_string());EXPECT_EQ(0u, s.Length()) << "ok";
}const char kHelloString[] = "Hello, world!";// Tests the c'tor that accepts a C string.
TEST(MyString, ConstructorFromCString) {const MyString s(kHelloString);EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));EXPECT_EQ(sizeof(kHelloString) / sizeof(kHelloString[0]) - 1, s.Length());fprintf(stderr, "Print Info: sizeof(kHelloString): %d, sizeof(kHelloString[0]): %d, s.Length(): %d\n",sizeof(kHelloString), sizeof(kHelloString[0]), s.Length());
}// Tests the copy c'tor.
TEST(MyString, CopyConstructor) {const MyString s1(kHelloString);const MyString s2 = s1;EXPECT_EQ(0, strcmp(s2.c_string(), kHelloString));
}// Tests the Set method.
TEST(MyString, Set) {MyString s;s.Set(kHelloString);EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));// Set should work when the input pointer is the same as the one// already in the MyString object.s.Set(s.c_string());ASSERT_EQ(0, strcmp(s.c_string(), kHelloString));// Can we set the MyString to NULL?s.Set(NULL);EXPECT_STREQ(NULL, s.c_string());
}
test_TEST_F.cpp:
#include <iostream>
#include <vector>
#include <string>
#include <time.h>
#include <gtest/gtest.h>
#include "funset.hpp"// reference: googletest/samples/
// To use a test fixture, derive a class from testing::Test.
class QueueTest : public testing::Test {
protected: // You should make the members protected s.t. they can be// accessed from sub-classes.// virtual void SetUp() will be called before each test is run. You// should define it if you need to initialize the varaibles.// Otherwise, this can be skipped.virtual void SetUp() override {q1_.Enqueue(1);q2_.Enqueue(2);q2_.Enqueue(3);}// virtual void TearDown() will be called after each test is run.// You should define it if there is cleanup work to do. Otherwise,// you don't have to provide it.//// virtual void TearDown() {// }// A helper function that some test uses.static int Double(int n) {return 2 * n;}// A helper function for testing Queue::Map().void MapTester(const Queue<int> * q) {// Creates a new queue, where each element is twice as big as the// corresponding one in q.const Queue<int> * const new_q = q->Map(Double);// Verifies that the new queue has the same size as q.ASSERT_EQ(q->Size(), new_q->Size());// Verifies the relationship between the elements of the two queues.for (const QueueNode<int> * n1 = q->Head(), *n2 = new_q->Head();n1 != NULL; n1 = n1->next(), n2 = n2->next()) {EXPECT_EQ(2 * n1->element(), n2->element());}delete new_q;}// Declares the variables your tests want to use.Queue<int> q0_;Queue<int> q1_;Queue<int> q2_;
};// When you have a test fixture, you define a test using TEST_F instead of TEST.// Tests the default c'tor.
TEST_F(QueueTest, DefaultConstructor) {// You can access data in the test fixture here.EXPECT_EQ(0u, q0_.Size());
}// Tests Dequeue().
TEST_F(QueueTest, Dequeue) {int * n = q0_.Dequeue();EXPECT_TRUE(n == NULL);n = q1_.Dequeue();ASSERT_TRUE(n != NULL);EXPECT_EQ(1, *n);EXPECT_EQ(0u, q1_.Size());delete n;n = q2_.Dequeue();ASSERT_TRUE(n != NULL);EXPECT_EQ(2, *n);EXPECT_EQ(1u, q2_.Size());delete n;
}// Tests the Queue::Map() function.
TEST_F(QueueTest, Map) {MapTester(&q0_);MapTester(&q1_);MapTester(&q2_);
}/
// teaches how to reuse a test fixture in multiple test cases by deriving sub-fixtures from it
class QuickTest : public testing::Test {
protected:// Remember that SetUp() is run immediately before a test starts.// This is a good place to record the start time.virtual void SetUp() override {start_time_ = time(NULL);}// TearDown() is invoked immediately after a test finishes. Here we// check if the test was too slow.virtual void TearDown() override {// Gets the time when the test finishesconst time_t end_time = time(NULL);// Asserts that the test took no more than ~5 seconds. Did you// know that you can use assertions in SetUp() and TearDown() as// well?EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long.";}// The UTC time (in seconds) when the test startstime_t start_time_;
};// We derive a fixture named IntegerFunctionTest from the QuickTest
// fixture. All tests using this fixture will be automatically
// required to be quick.
class IntegerFunctionTest : public QuickTest {// We don't need any more logic than already in the QuickTest fixture.// Therefore the body is empty.
};// Now we can write tests in the IntegerFunctionTest test case.
// Tests Factorial()
TEST_F(IntegerFunctionTest, Factorial) {// Tests factorial of negative numbers.EXPECT_EQ(1, Factorial(-5));EXPECT_EQ(1, Factorial(-1));EXPECT_GT(Factorial(-10), 0);// Tests factorial of 0.EXPECT_EQ(1, Factorial(0));// Tests factorial of positive numbers.EXPECT_EQ(1, Factorial(1));EXPECT_EQ(2, Factorial(2));EXPECT_EQ(6, Factorial(3));EXPECT_EQ(40320, Factorial(8));
}// Tests IsPrime()
TEST_F(IntegerFunctionTest, IsPrime) {// Tests negative input.EXPECT_FALSE(IsPrime(-1));EXPECT_FALSE(IsPrime(-2));EXPECT_FALSE(IsPrime(INT_MIN));// Tests some trivial cases.EXPECT_FALSE(IsPrime(0));EXPECT_FALSE(IsPrime(1));EXPECT_TRUE(IsPrime(2));EXPECT_TRUE(IsPrime(3));// Tests positive input.EXPECT_FALSE(IsPrime(4));EXPECT_TRUE(IsPrime(5));EXPECT_FALSE(IsPrime(6));EXPECT_TRUE(IsPrime(23));
}//
// teaches how to reuse a test fixture in multiple test cases by deriving sub-fixtures from it
class QueueTest_2 : public QuickTest {
protected:virtual void SetUp() override {// First, we need to set up the super fixture (QuickTest).QuickTest::SetUp();// Second, some additional setup for this fixture.q1_.Enqueue(1);q2_.Enqueue(2);q2_.Enqueue(3);}// By default, TearDown() inherits the behavior of// QuickTest::TearDown(). As we have no additional cleaning work// for QueueTest, we omit it here.//// virtual void TearDown() override {// QuickTest::TearDown();// }Queue<int> q0_;Queue<int> q1_;Queue<int> q2_;
};// Tests the default constructor.
TEST_F(QueueTest_2, DefaultConstructor) {EXPECT_EQ(0u, q0_.Size());
}// Tests Dequeue().
TEST_F(QueueTest_2, Dequeue) {int* n = q0_.Dequeue();EXPECT_TRUE(n == NULL);n = q1_.Dequeue();EXPECT_TRUE(n != NULL);EXPECT_EQ(1, *n);EXPECT_EQ(0u, q1_.Size());delete n;n = q2_.Dequeue();EXPECT_TRUE(n != NULL);EXPECT_EQ(2, *n);EXPECT_EQ(1u, q2_.Size());delete n;
}
test_TEST.cpp(main function):
#include <iostream>
#include <vector>
#include <gtest/gtest.h>int main()
{std::vector<char*> argv{
#ifdef _DEBUG"E:/GitCode/Messy_Test/lib/dbg/x64_vc12/gtest_Test.exe",
#else"E:/GitCode/Messy_Test/lib/rel/x64_vc12/gtest_Test.exe",
#endif//"--gtest_repeat=2 ", // 重復運行測試次數//"--gtest_break_on_failure", // 遇到failure退出"--gtest_filter=*", // 指定需要運行的tests//"--gtest_print_time=0", // don't print the elapsed time of each test"--gtest_output=xml:E:/GitCode/Messy_Test/testdata/info.xml" // 結果輸出到指定的xml文件};int argc = argv.size();// Initializes Google Test. This must be called before calling RUN_ALL_TESTS()testing::InitGoogleTest(&argc, argv.data());// Use this function in main() to run all tests. It returns 0 if all// tests are successful, or 1 otherwiseint ret = RUN_ALL_TESTS();if (ret == 0) fprintf(stderr, "========== all tests are succeseful =========\n");else fprintf(stderr, "********** some tests are failed **********\n");return ret;
}
執行結果如下:
GitHub:https://github.com/fengbingchun/Messy_Test
總結
以上是生活随笔為你收集整理的gtest使用初级指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu14.04上编译指定版本的p
- 下一篇: 行列式介绍及Eigen/OpenCV/C