1.0 general part
Like common testing tools, gtest provides common tools and components for monomer testing. For example, judge whether the values of various types are equal, greater than, less than, etc., and manage multiple test groups, such as testcase under TestSuite. In order to facilitate the processing of initialization data and reduce duplicate code, setup and teardown functions are provided.
The official document says: TEST has two parameters: the test case name and the test name The first is the case name and the second is the test name. This is google's noun calling method. In fact, it is TestSuite and testcase in the general sense. The former is a group and the latter is a test case. In order to facilitate test management, several related tests are collectively referred to as a test group. This is a programming convention, but if you put irrelevant tests into a test_ case_ No error will be reported under name, but it is inconvenient to make test summary and summary.
# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
About TEST macro, this is a macro definition layer by layer. Although google programming specification says that macros are not recommended, gtest uses macros to create classes and functions. Using macros can give users a more concise interface and has advantages in efficiency, but it is very obscure to read. In general, in the basic tools and underlying API s, macros still have a large application space, because this part is basically not much changed. The big cattle who write the underlying tools have the ability to control this unconventional writing method.
Using macros to realize the repetitive work at the bottom or encapsulate complex interfaces is a common way in open source projects.
-
Sample #1 shows the basic steps of using googletest to test C++ functions.
-
Sample #2 shows a more complex unit test for a class with multiple member functions.
-
Sample #3 uses a test fixture.
-
Sample #4 teaches you how to use googletest and googletest.h together to get the best of both libraries.
-
Sample #5 puts shared testing logic in a base test fixture, and reuses it in derived fixtures.
-
Sample #6 demonstrates type-parameterized tests.
-
Sample #7 teaches the basics of value-parameterized tests.
-
Sample #8 shows using Combine() in value-parameterized tests.
-
Sample #9 shows use of the listener API to modify Google Test's console output and the use of its reflection API to inspect test results.
-
Sample #10 shows use of the listener API to implement a primitive memory leak checker.
1.1 sample1
The official sample1 has two functions, factorial function int Factorial() and judging prime function bool IsPrime(int n).
The test case is divided into two TestSuite.
FactorialTest contains three test cases of factorial functions:
-
Negative: enter a negative number to test the factorial
-
Zero: Enter 0 to test factorial
-
Positive: Test factorial with positive input
TEST(FactorialTest, Negative) { // This test is named "Negative", and belongs to the "FactorialTest" // test case. EXPECT_EQ(1, Factorial(-5)); EXPECT_EQ(1, Factorial(-10)); 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)); }
IsPrimeTest contains three test cases of prime detection functions:
-
Negative: enter negative number and limit value INT_MIN
-
Trivial: enter several special values, such as the number of critical points
-
Positive: enter a positive number
// 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)); }
gtest can be run directly, and it can be executed without the main function in the code. The output indicates that gtest is used_ main. CC function.
The output shows that six use cases from two testcase s were executed. The two cases are the test group FactorialTest and IsPrimeTest.
You can add your own main function and call RUN_ALL_TESTS() executes the test case.
int main(int argc, char* argv[]) { cout << "start gtest demo \r\n" << endl; ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
1.2 sample2
The official sample 2 tests a class called MyString, including test constructors and member functions.
This class has the following characteristics: the default constructor takes the member variable C_ The string pointer is initialized to nullptr, and the constructor MyString receives a char * string and copies it to C through the Set function_ string_.
class MyString { private: const char* c_string_; const MyString& operator=(const MyString& rhs); public: // Clones a 0-terminated C string, allocating memory using new. // Class method static const char* CloneCString(const char* a_c_string); // The default c'tor constructs a NULL string. // Default constructor MyString() : c_string_(nullptr) {} // Constructs a MyString by cloning a 0-terminated C string. // Constructor, prohibit implicit conversion explicit MyString(const char* a_c_string) : c_string_(nullptr) { Set(a_c_string); } // Copy c'tor // copy constructor MyString(const MyString& string) : c_string_(nullptr) { 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_ == nullptr ? 0 : strlen(c_string_); } // Sets the 0-terminated C string this MyString object represents. // Member function void Set(const char* c_string); };
Class method and Set member function.
// Clones a 0-terminated C string, allocating memory using new. const char* MyString::CloneCString(const char* a_c_string) { if (a_c_string == nullptr) return nullptr; 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; }
For test cases, a test testcase called MyString is constructed, which contains four test cases.
The first use case: TEST(MyString, DefaultConstructor), test the default constructor, mystring (): C_ string_ (nullptr) {}.
const MyString s; EXPECT_STREQ(nullptr, s.c_string()); EXPECT_EQ(0u, s.Length());
The second use case: TEST(MyString, ConstructorFromCString). Test the MyString(const char* a_c_string) constructor. sizeof(kHelloString)-1 and s.Length() are equal because this is a C-type string, and the end is \ 0. sizeof calculates the space allocated to this string.
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()); }
The third use case: TEST(MyString, CopyConstructor), testing the copy constructor.
// Tests the copy c'tor. TEST(MyString, CopyConstructor) { const MyString s1(kHelloString); const MyString s2 = s1; EXPECT_EQ(0, strcmp(s2.c_string(), kHelloString)); }
The fourth use case: TEST(MyString, Set), test the Set member function.
// 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()); EXPECT_EQ(0, strcmp(s.c_string(), kHelloString)); // Can we set the MyString to NULL? s.Set(nullptr); EXPECT_STREQ(nullptr, s.c_string()); }
The final running results are shown in the figure below. One test case and four tests are all successful.
1.3 sample3
The official sample 3 shows the concept of the test fixture, prepares the environment for the test, and each test case uses the same environment initialization data. Sample 3 tests a Queue template class written by itself. This Q implements a one-way linked list. The element item is implemented by template < typename E > class queuenode, and there is a friend class Queue < E > inside. The Queue class has a default constructor and the following members:
-
SIze() -- size
-
Head() -- queue header
-
Last() -- end of queue
-
Void enqueue (const E & element) -- join the team
-
E* Dequeue() -- dequeue, return the element of dequeue
-
Queue* Map(F function) const -- copy the queue and perform function operations on the elements. For example, in the test, multiply the elements by twice to join the queue, and each element returning the new queue is twice the size of the old queue element.
The sample3 example shows the concept of "test fixture". The "test fixture" is to realize the preparation before test, such as creating a series of common functions and data. These common conditions can be referenced before each test case runs. The most common is to initialize Setup or deal with the TearDown function, so using "test fixture" can avoid repeated code.
How does test fixture write the public part
-
There are no restrictions on the class name of the test fixture, which can be named according to the test requirements. This class needs to inherit the testing::Test class, class queuetestsmpl3: public testing::Test, and override the SetUp and TearDown methods in the test fixture class.
-
If a TEST fixture is used, the TEST case name cannot be created by TEST, but by TEST_F to create, in TEST_ Write the class name of the TEST fixture in the first parameter of the f macro.
-
After the test is started, the test fixture class will be run before each test case is executed. Achieve the purpose of preparing a test environment.
For example, add a sentence to the test fixture to print:
void SetUp() override { std::cout << "this test fixture" << std::endl; q1_.Enqueue(1); q2_.Enqueue(2); q2_.Enqueue(3); }
Three test cases will call setup3 times.
Each TEST_F will create a class and inherit the test fixture class. For example, TEST_F(QueueTestSmpl3, DefaultConstructor) will be extended to:
class QueueTestSampl3_DefaultConstructor_Test:public QueueTestSmpl3 {}
So every test_ When f runs, the QueueTestSmpl3 class will be called once.
The second use case is analyzed below:
// Tests Dequeue(). TEST_F(QueueTestSmpl3, Dequeue) { int * n = q0_.Dequeue(); // q0 There are no elements in the queue, setup Not set q0,Out of the team will only be nullptr EXPECT_TRUE(n == nullptr); n = q1_.Dequeue(); // q1 There is one element for the column: 1 ASSERT_TRUE(n != nullptr); EXPECT_EQ(1, *n); EXPECT_EQ(0u, q1_.Size()); // After leaving the team, q1 The queue has no elements delete n; n = q2_.Dequeue(); // q2 stay setup Time to enter 2,3 Two elements ASSERT_TRUE(n != nullptr); EXPECT_EQ(2, *n); EXPECT_EQ(1u, q2_.Size()); delete n; }
1.4 sample4
The official sample 4 tests a Counter class, which implements two functions: Increment and Decrement. One value of int type increases and the other decreases. When the value is 0, it will not decrease and return 0 directly.
TEST(Counter, Increment) { Counter c; // Test that counter 0 returns 0 EXPECT_EQ(0, c.Decrement()); // EXPECT_EQ() evaluates its arguments exactly once, so they // can have side effects. EXPECT_EQ(0, c.Increment()); EXPECT_EQ(1, c.Increment()); EXPECT_EQ(2, c.Increment()); EXPECT_EQ(3, c.Decrement()); }
The test is very simple. Note that it is still 0 after the first call of c.Increment(), because the return value of the Incremen function is written into the temporary variable first, and then the + + operation is executed. After 3 additions, c.counter_= 3. The subtraction method returns 3, and then c.counter_= two
int Counter::Increment() { return counter_++; }
1.5 sample5
sample 3 shows the concept of test fixture, which can easily create common parts for each test case, such as preparing test environment and data. However, if the environment required by multiple tests is similar and there are only minor differences, the common parts can be extracted and put into the base class -- create a super test fixture, and their different test fixtures are personalized by inheritance -- to derive their own test fixtures.
sample 5 first creates a super test fixture called QuickTest, which inherits the testing::Test class. QuickTest calculates the execution time of each test case in a simple way. Start is recorded in SetUp_ Time, TearDown records end_time, subtraction is the execution time. If you deliberately Sleep (6) in the test, the timeout error will be displayed as follows:
The test cases are as follows:_ F uses the IntegerFunctionTest class as the test_fixture name, and IntegerFunctionTest class inherits from QuickTest, so time can also be calculated.
class IntegerFunctionTest : public QuickTest { // We don't need any more logic than already in the QuickTest fixture. // Therefore the body is empty. }; TEST_F(IntegerFunctionTest, Factorial) { // **** Factorial function tests } TEST_F(IntegerFunctionTest, IsPrime) { // **** Judging the of prime function tests }
The second test case shows the method of sharing test fixtures. For example, in sample 3, when testing the Queue, you need to initialize the Queue. Then you can initialize the pair of columns in the test fixture class and inherit from the QuickTest class. Then the test case can perform the function of counting the execution time when running.
class QueueTest : public QuickTest { protected: 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() { // QuickTest::TearDown(); // } Queue<int> q0_; Queue<int> q1_; Queue<int> q2_; };
The test case part is the same as sample 3, but because the test fixture class inherits QuickTest, this test case can count the execution time and execute the super error report in the TearDown function.
The final execution result is shown in the figure below. There are two test case s: IntegerFunctionTest and QueueTest. Each case has two tests.