GoogleTest is a test framework open sourced by Google. Using this framework, we can easily test our projects;
This article describes the basic use of GoogleTest;
Source code:
Unit testing with GoogleTest in C++
Install and configure GoogleTest
Thanks to vcpkg, we can install and configure the GoogleTest library very easily;
vcpkg install gtest
Note:
- The name of GoogleTest is
gtest
; - You may need to install the x64 version;
After the installation is complete, according to the prompts, add the configuration to our CMake project and add the link library for our executable file:
add_executable(main_test main_test.cc)find_package(GTest CONFIG REQUIRED)target_link_libraries(main_test PRIVATE GTest::gmock GTest::gtest GTest::gmock_main GTest::gtest_main)
At this point, the configuration is complete;
About how to configure vcpkg to install 64-bit by default:
GoogleTest official documentation:
The first GoogleTest example
Next we create a single test file;
main_test.cc
#include <iostream>#include "gtest/gtest.h"TEST(HelloTest, PrintHello) { std::string str{"Hello, World!"}; ASSERT_EQ(str, "Hello, World!"); ASSERT_EQ(str.size(), 13);}int main(int argc, char **argv) { printf("Running main() from %s\n", __FILE__); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();}
The code first introduces the header file gtest/gtest.h
, which contains the core macros in GoogleTest, etc.;
Then we used the TEST()
macro to create a unit test case;
The TEST()
macro is used as follows:
TEST(test_suite_name, test_case_name) { // test body ...}
The first parameter is the name of the entire test group, and the second parameter is the name of a specific use case in the test group;
And finally in the main function:
First output the file location of the unit test, then initialize the test with the parameters specified when starting the test, and finally call RUN_ALL_TESTS();
start the test;
The results after starting the test are as follows:
/Users/jasonkayzk/self-workspace/cpp-learn/cmake-build-debug/main_testRunning main() from /Users/jasonkayzk/self-workspace/cpp-learn/main_test.cc[==========] Running 1 test from 1 test suite.[----------] Global test environment set-up.[----------] 1 test from HelloTest[ RUN ] HelloTest.PrintHello[ OK ] HelloTest.PrintHello (0 ms)[----------] 1 test from HelloTest (0 ms total)[----------] Global test environment tear-down[==========] 1 test from 1 test suite ran. (0 ms total)[ PASSED ] 1 test.
execution succeed!
assertion
gtest provides a large number of test assertion functions, which are roughly divided into two categories:
-
ASSERT_*
: execution fails, exit the current test function and return immediately (note: not exit the current case); -
EXPECT_*
: If the execution fails, it will not exit the current test function and continue to execute downward;
Two examples are given below:
TEST(ExpectAndAssert, ExpectTest) { auto add = [](const int x, const int y) { return x + y; }; EXPECT_EQ(add(1, 2), 4); EXPECT_EQ(add(1, 2), 3);}TEST(ExpectAndAssert, AssertTest) { auto subtract = [](const int x, const int y) { return x - y; }; ASSERT_EQ(subtract(3, 1), 3); ASSERT_EQ(subtract(3, 1), 2);}
Output after execution:
[==========] Running 2 tests from 1 test suite.[----------] Global test environment set-up.[----------] 2 tests from ExpectAndAssert[ RUN ] ExpectAndAssert.ExpectTest/Users/kylinkzhang/self-workspace/cpp-learn/main_test.cc:15: FailureExpected equality of these values: add(1, 2) Which is: 3 4[ FAILED ] ExpectAndAssert.ExpectTest (0 ms)[ RUN ] ExpectAndAssert.AssertTest/Users/kylinkzhang/self-workspace/cpp-learn/main_test.cc:22: FailureExpected equality of these values: subtract(3, 1) Which is: 2 3[ FAILED ] ExpectAndAssert.AssertTest (0 ms)[----------] 2 tests from ExpectAndAssert (0 ms total)[----------] Global test environment tear-down[==========] 2 tests from 1 test suite ran. (0 ms total)[ PASSED ] 0 tests.[ FAILED ] 2 tests, listed below:[ FAILED ] ExpectAndAssert.ExpectTest[ FAILED ] ExpectAndAssert.AssertTest 2 FAILED TESTS
Some of the more commonly used assertions are:
1. Boolean value check:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_TRUE( condition ); | EXPECT_TRUE( condition ); | condition is true |
ASSERT_FALSE( condition ); | EXPECT_FALSE( condition ); | condition is false |
2. Numerical data check:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_EQ( expected , actual ); | EXPECT_EQ( expected , actual ); | expected == actual |
ASSERT_NE( val1 , val2 ); | EXPECT_NE( val1 , val2 ); | val1 != val2 |
ASSERT_LT( val1 , val2 ); | EXPECT_LT( val1 , val2 ); | val1 < val2 |
ASSERT_LE( val1 , val2 ); | EXPECT_LE( val1 , val2 ); | val1 <= val2 |
ASSERT_GT( val1 , val2 ); | EXPECT_GT( val1 , val2 ); | val1 > val2 |
ASSERT_GE( val1 , val2 ); | EXPECT_GE( val1 , val2 ); | val1 >= val2 |
3. String comparison:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_STREQ( expected_str , actual_str ); | EXPECT_STREQ( expected_str , actual_str ); | Two C strings have the same content |
ASSERT_STRNE( str1 , str2 ); | EXPECT_STRNE( str1 , str2 ); | Two C strings have different contents |
ASSERT_STRCASEEQ( expected_str , actual_str ); | EXPECT_STRCASEEQ( expected_str , actual_str ); | Two C strings have the same content, ignoring case |
ASSERT_STRCASENE( str1 , str2 ); | EXPECT_STRCASENE( str1 , str2 ); | Two C strings have different contents, ignoring case |
4. Exception check:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_THROW( statement , exception_type ); | EXPECT_THROW( statement , exception_type ); | statement throws an exception of the given type |
ASSERT_ANY_THROW( statement ); | EXPECT_ANY_THROW( statement ); | statement throws an exception of any type |
ASSERT_NO_THROW( statement ); | EXPECT_NO_THROW( statement ); | statement doesn’t throw any exception |
5. Floating point check:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_FLOAT_EQ( expected, actual ); | EXPECT_FLOAT_EQ( expected, actual ); | the two float values are almost equal |
ASSERT_DOUBLE_EQ( expected, actual ); | EXPECT_DOUBLE_EQ( expected, actual ); | the two double values are almost equal |
Compare two numbers that are close:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_NEAR( val1, val2, abs_error ); | EXPECT_NEAR (val1, val2, abs_error ); | the difference between val1 and val2 doesn’t exceed the given absolute error |
6. Type comparison assertion:
This class asserts only one ::testing::StaticAssertTypeEq<T, T>()
:
- It doesn’t do anything when the types are the same;
- If it is different, it will cause a compilation error;
It should be noted that: make the code trigger the compiler to deduce the type, otherwise a compilation error will also occur;
Such as:
template <typename T> class Foo { public: void Bar() { ::testing::StaticAssertTypeEq<int, T>(); }};
The following code will not cause compilation conflicts:
void Test1() { Foo<bool> foo; }
But the following code triggers a compilation error because it triggers type deduction by the compiler:
void Test2() { Foo<bool> foo; foo.Bar(); }
7. Several special assertions:
-
SUCCEED()
macro: Directly mark the assertion success; -
FAIL()
macro: mark a fatal error (same asASSERT_*
); -
ADD_FAILURE()
macro: mark non-fatal errors (same asEXPECT_*
);
See the official documentation for more assertions:
custom error message
Sometimes, we may not be satisfied with the wrong output by default:
E.g:
FailureExpected equality of these values: 1+2 Which is: 3 4
At this point, we can also use the <<
operator to customize the output information;
TEST(TestMessage, Message) { int result = 4; EXPECT_EQ(1 + 2, result) << "1+2 should equals to: " << result;}
At this point the output:
FailureExpected equality of these values: 1 + 2 Which is: 3 result Which is: 41+2 should equals to: 4
From the output, we can clearly see that the result is 4 at this time!
Event mechanism and TEST_F
Friends who have used JUnit should be familiar with @Before
and @After
annotations;
They allow us to perform some operations before starting the use case and at the end of the use case;
gtest also provides such events, and they are divided into various types:
- global events;
- TestSuite event;
- TestCase event;
Let’s take a look at them one by one;
global event
To implement global events, you must write a class to inherit the testing::Environment
class and implement the SetUp
and TearDown
methods inside;
After this:
-
SetUp()方法
: executed before all cases are executed; -
TearDown()方法
: Executed after all cases are executed;
At the same time, you also need to tell gtest to add this global event:
The event needs to be hooked in through the testing::AddGlobalTestEnvironment method in the main function;
This also means that we can write many such classes, and then hang their events;
TestSuite events
We need to write a class that extends testing::Test
and implements two static methods:
-
SetUpTestCase()方法
: executed before the first TestCase; -
TearDownTestCase()方法
: executed after the last TestCase;
When writing test cases, we need to use the TEST_F
macro, the first parameter must be the name of our class above, representing a TestSuite;
TestCase events
The TestCase event is hung before and after each case is executed. The implementation is almost the same as the above, but the SetUp method and the TearDown method need to be implemented:
-
SetUp()方法
: executed before each TestCase; -
TearDown()方法
: executed after each TestCase;
The event mechanism can help us simplify testing, for example:
We can use the event mechanism to share data between test functions;
An example of using various events is provided below:
class GlobalEvent : public testing::Environment {public: void SetUp() override { std::cout << "Before any case, Global" << std::endl; } void TearDown() override { std::cout << "After all cases done, Global" << std::endl; }};class VectorTest : public ::testing::Test {protected: // set resources before test void SetUp() override { vec.push_back(1); vec.push_back(2); vec.push_back(3); } // clean up resources after test void TearDown() override { vec.clear(); } static void SetUpTestCase() { std::cout << "SetUpTestCase()" << std::endl; } static void TearDownTestCase() { std::cout << "TearDownTestCase()" << std::endl; } std::vector<int> vec;};// Here we are using TEST_F, not TESTTEST_F(VectorTest, PushBack) { // We changed vec here, but this is invisible to other test cases vec.push_back(4); EXPECT_EQ(vec.size(), 4); EXPECT_EQ(vec.back(), 4);}TEST_F(VectorTest, Size) { ASSERT_EQ(vec.size(), 3);}int main(int argc, char **argv) { printf("Running main() from %s\n", __FILE__); ::testing::AddGlobalTestEnvironment(new GlobalEvent); // add env ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();}
First, we define the GlobalEvent
class, which inherits testing::Environment
to define the operations before and after the start of the entire test;
Then, we define the VectorTest
class, which extends testing::Test
to define the operations before and after the start of this test group and individual test collections;
Next, we use TEST_F
define two sets of test cases;
Finally, we register the environment variables we defined earlier in the main function: ::testing::AddGlobalTestEnvironment(new GlobalEvent);
;
Executing the use case, we get the following output:
Running main() from /Users/jasonkayzk/self-workspace/cpp-learn/main_test.cc[==========] Running 2 tests from 1 test suite.[----------] Global test environment set-up.Before any case, Global[----------] 2 tests from VectorTestSetUpTestCase()[ RUN ] VectorTest.PushBack[ OK ] VectorTest.PushBack (0 ms)[ RUN ] VectorTest.Size[ OK ] VectorTest.Size (0 ms)TearDownTestCase()[----------] 2 tests from VectorTest (0 ms total)[----------] Global test environment tear-downAfter all cases done, Global[==========] 2 tests from 1 test suite ran. (0 ms total)[ PASSED ] 2 tests.
Note: In the two tests above:
// Here we are using TEST_F, not TESTTEST_F(VectorTest, PushBack) { // We changed vec here, but this is invisible to other test cases vec.push_back(4); EXPECT_EQ(vec.size(), 4); EXPECT_EQ(vec.back(), 4);}TEST_F(VectorTest, Size) { ASSERT_EQ(vec.size(), 3);}
Modifying data in one test function will not affect other test functions;
This is because, each individual test case calls our overloaded SetUp
and TearDown
functions individually!
Appendix
Reference article:
- https://zhuanlan.zhihu.com/p/369466622
- http://senlinzhan.github.io/2017/10/08/gtest/
- https://www.cnblogs.com/coderzh/archive/2009/04/06/1430364.html
- https://www.cnblogs.com/helloworldcode/p/9606838.html
Source code:
This article is reprinted from: https://jasonkayzk.github.io/2022/05/09/C++%E4%B8%AD%E4%BD%BF%E7%94%A8GoogleTest%E8%BF%9B%E8%A1%8C %E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/
This site is for inclusion only, and the copyright belongs to the original author.