Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

Google unit test framework GTEST official sample note 1 -- simple use case

Ah, harpon 2021-02-23 19:22:44 阅读数:2 评论数:0 点赞数:0 收藏数:0

1.0 General part

Just like common testing tools ,gtest Provides common tools and components for monomer testing . For example, we can judge that all kinds of values are equal , Greater than , Less than equal , Test groups that manage multiple tests, such as testsuit Under jurisdiction testcase, In order to facilitate the processing of initialization data and reduce duplicate code , Provides setup and teardown function .

Official document says :TEST has two parameters: the test case name and the test name. The first is case name , The second is test name , This is a google It's a way of addressing people , In fact, it is in the general sense testsuit and testcase, The former is a group , The latter is the test case , To facilitate test management , Put together several related tests and call them a test group . It's a programming convention , But if you put irrelevant tests in one test_case_name Next , No errors reported , It's just that it's not convenient to do test summary and summary .

# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)

About TEST macro , This is a layer by layer macro definition , although google The programming specification says macro is not recommended , however gtest But a lot of macros are used to create classes and functions , Using macros can give users a more concise interface , There are also advantages in efficiency , But it's hard to read . General , In the basic tools and at the bottom API in , Macro is still made up of vast application space , Because this part basically doesn't change much , Those who write low-level tools have the ability to handle this unconventional way of writing .

Using macros to achieve the underlying repetitive work or encapsulate complex interfaces , It's very common 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

official sample1 Yes 2 A function , Factorial function int Factorial() And judging prime functions bool IsPrime(int n).

The test cases are divided into 2 individual testsuit.

FactorialTest contain 3 A test case for factorial function :

  1. Negative: Enter a negative number to test the factorial

  2. Zero: Input is 0 Test factorials

  3. Positive: Enter a positive number to test factorial

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 contain 3 A test case for a prime detection function :

  1. Negative: The input is negative and the limit value INT_MIN

  2. Trivial: Enter several special values, such as the number of critical points

  3. Positive: The input is 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 Just run directly , There is no main Functions can also be executed . The output indicates that gtest_main.cc function .

The output is displayed from 2 individual testcase Of 6 A use case is executed . Two case It's the test group FactorialTest and IsPrimeTest.

You can add your own main function , call RUN_ALL_TESTS() Execute 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

official sample 2, Test one called MyString Class , Including test constructor and member functions .

This class has the following characteristics : The default constructor takes the member variable c_string The pointer is initialized to nullptr, Constructors MyString Receive one char * The string is then passed through Set Function is copied to c_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.
// Constructors , Disable 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 functions 
void Set(const char* c_string);
};

Class method implementation and Set The realization of 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;
}

The test case , Built a test testcase be called MyString, Contains 4 individual test Use 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 MyString(const char* a_c_string) Constructors ,sizeof(kHelloString)-1 and s.Length() equal , It's because it's c String of type , The end is \0,sizeof The calculation is the space allocated to the 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), Test 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 Set Member functions .

// 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 result is shown in the figure below ,1 individual test case,4 individual tests, All success .

 

 

1.3 sample3

official sample 3, Shows the concept of test fixture , Prepare the environment for testing , Every test case All use the same environment to initialize data, etc .sample 3 Tested a self compiled Queue Template class , This Q Realized a one-way linked list . Element items use template <typename E> class QueueNode Realization , There are friends inside Queue<E>. queue Queue Class has a default constructor and the following members :

  • SIze() -- size

  • Head() -- Queue header

  • Last() -- Queue tail

  • void Enqueue(const E& element) -- The team

  • E* Dequeue() -- Out of the team , Return out of team elements

  • Queue* Map(F function) const -- Achieve queue copy , And execute... On the element function operation , For example, in the test, we multiply the elements by 2 Bei joined the team , Each element that returns the new queue is twice the size of the old queue element .

sample3 Examples show “test fixture”( Test fixture ) The concept of ,“test fixture” It's the preparation before testing , For example, creating a series of shared functions and data , Every test case These common conditions can be referenced before running . The most common is initialization Setup Or after treatment TearDown function , So use “test fixture” Can avoid duplicate code .

test fixture How to write the public part

  1. test fixture There is no limit to the class name of , You can name it according to the test requirements , This class needs to inherit testing::Test class ,class QueueTestSmpl3 : public testing::Test , Rewrite... In the test fixture class SetUp and TearDown Method .

  2. If a test fixture is used , Then the test case name cannot be used TEST To create , But use TEST_F To create , stay TEST_F In the first parameter of the macro , Write test fixture class name .

  3. After the test starts , Every test case The test fixture class will be run before execution . Achieve the goal of preparing a test environment .

For example, add a print sentence to the test fixture :

 void SetUp() override {
std::cout << "this test fixture" << std::endl;
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}

Three test cases , Would call setup3 Time .

Every TEST_F Will create a class , And inheritance test fixture class . for example TEST_F(QueueTestSmpl3, DefaultConstructor) Will be extended to :

class QueueTestSampl3_DefaultConstructor_Test:public QueueTestSmpl3 {}

So each TEST_F Runtime , Will be called once QueueTestSmpl3 class .

Let's analyze the second use case :

// Tests Dequeue().
TEST_F(QueueTestSmpl3, Dequeue) {
int * n = q0_.Dequeue(); // q0 The queue has no elements ,setup No settings q0, Leaving the team will only be nullptr
EXPECT_TRUE(n == nullptr);
​
n = q1_.Dequeue(); // q1 There is an element for the column :1
ASSERT_TRUE(n != nullptr);
EXPECT_EQ(1, *n);
EXPECT_EQ(0u, q1_.Size()); // After leaving the team ,q1 There are no elements in the queue 
delete n;
​
n = q2_.Dequeue(); // q2 stay setup It's 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

official sample 4 Tested one Counter class , This class implements Increment and Decrement Two functions , One int The value of type increases , A self subtraction , The value is 0 The time is no longer minus and returns directly 0.

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 simple , Notice that for the first time c.Increment() After calling , Still 0, Is due to Incremen Function writes the return value to the temporary variable first , And then execute ++ operation .3 After the execution of a plus ,c.counter_ = 3, The minus method returns 3, then c.counter_=2.

int Counter::Increment() {
return counter_++;
}

 

1.5 sample5

sample 3 Shows the concept of test fixture , It is convenient to create common parts for each test case , For example, preparing test environment and data . But if multiple tests require similar environments , There are only small differences , Then you can pull out the shared parts and put them into the base class -- Create a super test fixture, The different test fixtures are personalized by inheritance -- Derived from their own test fixture.

sample 5 First, we created a super test fixture , Class name QuickTest, Inherit testing::Test class ,QuickTest Calculate each test case Execution time of , It's very simple ,SetUp Li record start_time,TearDown Li record end_time, Subtraction is execution time . If on purpose test in Sleep(6), The error will be displayed as follows :

The test cases are as follows ,TEST_F Use IntegerFunctionTest Class as test_fixture name , and IntegerFunctionTest Class inherited from QuickTest, So you can also calculate time .

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) {
// **** Of factorial functions tests
}
​
TEST_F(IntegerFunctionTest, IsPrime) {
// **** Judge the function of prime number tests
}

The second test case Shows how to share test fixtures , Such as sample 3 in , test Queue When , The queue needs to be initialized , Well, it can be in this test fixture Class , And inherited from QuickTest class , So the test case The function of counting execution time can be performed at runtime .

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_;
};

test case Part and sample 3 equally , But because of test fixture Class inherited QuickTest, This test case can count the execution time , And perform TearDown The supererror in the function .

The final execution result is shown in the figure below . There are two test case:IntegerFunctionTest and QueueTest, Every case Yes 2 individual tests.

 

Copyright statement
In this paper,the author:[Ah, harpon],Reprint please bring the original link, thank you

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;