Published on

C++ Project: Googletest Unit Testing

Authors
  • avatar
    Name
    light-city

C++ Project: Googletest Unit Testing

Table of Contents

1. Integrating Googletest Unit Testing into Clion

Googletest - Google Testing and Mocking Framework

Source code download link:

https://github.com/google/googletest/releases

Googletest contains two modules, gtest and gmock, which need to be compiled separately. After compilation, copy the includes folder and .so files to the specified locations under the following path.

Then create a directory named ext in the project, which is divided into two parts:

  • The first part: includes

    Contains include files for gmock and gtest, corresponding to the -I parameter in gcc, which is the header file path, containing header files ending with .h.

  • The second part: libs

    Contains libs files for gmock and gtest, corresponding to the -L parameter in gcc, which is the library file path, containing .so or .dll dynamic or static linking libraries.

├── ext
│   ├── includes
│   │   ├── gmock
│   │   └── gtest
│   └── libs
│       ├── gmock
│       └── gtest

Once the local project path configuration is set up, configure it in CMakeLists.txt:

# ext
set(BASE_INCLUDES ext/includes)
set(BASE_LIBS ext/libs)

# googletest
set(GTEST_INCLUDE_DIR ${BASE_INCLUDES}/gtest/include)
set(GMOCK_INCLUDE_DIR ${BASE_INCLUDES}/gmock/include)
set(GTEST_LINK_DIR ${BASE_LIBS}/gtest/lib/)
set(GMOCK_LINK_DIR ${BASE_LIBS}/gmock/lib/)

# Where to find header files, equivalent to the -I parameter in gcc/clang
include_directories(${GTEST_INCLUDE_DIR})
include_directories(${GMOCK_INCLUDE_DIR})

# Where to find library files, .so .dll .dylib, equivalent to the -L parameter in gcc
link_directories(${GTEST_LINK_DIR})
link_directories(${GMOCK_LINK_DIR})

Then, link the appropriate libraries for each test.

For example, if there's a test file named l1.cpp, configure it in CMakeLists.txt as follows:

add_executable(l1 l1.cpp)
target_link_libraries(l1 gtest gtest_main)  

2. HelloWorld with Googletest

int main(int argc, char* argv[])
{
    // Accept command line arguments
    testing::InitGoogleTest(&argc, argv);
    vector<int> x={1,2,3,5};
    // Using EXPECT_EQ
    for (int i = 0; i < x.size(); ++i)
    {
        EXPECT_EQ(x[i], 1) << "Vectors x and y differ at index " << i;
    }
    RUN_ALL_TESTS();
  
    return 0;
}

For example, the above is a complete example. Each time you run:

testing::InitGoogleTest(&argc, argv);

And then use:

RUN_ALL_TESTS();

to run all tests. Refer to the assertions below for each test.

Let's see the output:

l1.cpp:113: Failure
Expected equality of these values:
  x[i]
    Which is: 2
  1
Vectors x and y differ at index 1
l1.cpp:113: Failure
Expected equality of these values:
  x[i]
    Which is: 3
  1
Vectors x and y differ at index 2
l1.cpp:113: Failure
Expected equality of these values:
  x[i]
    Which is: 5
  1
Vectors x and y differ at index 3
[==========] Running 0 tests from 0 test cases.
[==========] 0 tests from 0 test cases ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 0 tests, listed below:

 0 FAILED TESTS

3. Introduction to Googletest Assertions

Google has encapsulated a series of macros EXPECT and ASSERT, where the difference between the EXPECT series and ASSERT series is:

  • EXPECT: continues execution if failed.
  • ASSERT: returns directly in the current function if failed, and the statements after ASSERT will not be executed.

For example:

TEST(ASSERTTest, ASSERT_VS_EXPECT)
{
    ASSERT_TRUE(10<2);  // Directly returns in the current function if failed, and the statements below will not be executed
    ASSERT_FALSE(10<2);
    EXPECT_EQ(6, Foo(310, 18));
}

Each test is a macro, and the underlying source code of Test is as follows:

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

Corresponding to the test case name and test name.

The assertion types are as follows:

Fatal assertionNonfatal assertionVerifies
ASSERT_TRUE(condition);EXPECT_TRUE(condition);condition is true
ASSERT_FALSE(condition);EXPECT_FALSE(condition);condition is false

For numeric data checks:

Fatal assertionNonfatal assertionVerifies
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 |

For string checks:

TEST(StringCmpTest, Demo)
{
    char* pszCoderZh = "CoderZh";
    wchar_t* wszCoderZh = L"CoderZh";
    std::string strCoderZh = "CoderZh";
    std::wstring wstrCoderZh = L"CoderZh";

    EXPECT_STREQ("CoderZh", pszCoderZh);
    EXPECT_STREQ(L"CoderZh", wszCoderZh);

    EXPECT_STRNE("CnBlogs", pszCoderZh);
    EXPECT_STRNE(L"CnBlogs", wszCoderZh);

    EXPECT_STRCASEEQ("coderzh", pszCoderZh);
    //EXPECT_STRCASEEQ(L"coderzh", wszCoderZh);    Not supported

    EXPECT_STREQ("CoderZh", strCoderZh.c_str());
    EXPECT_STREQ(L"CoderZh", wstrCoderZh.c_str());
}

Where STREQ and STRNE support both char* and wchar_t* types, while STRCASEEQ and STRCASENE only accept char*.

For explicit success or failure:

Success: SUCCEED();

Failure:

Fatal assertionNonfatal assertion
FAIL();ADD_FAILURE();
// Success or Failure
TEST(ExplicitTest, Demo)
{
    ADD_FAILURE() << "This line fails Sorry"; // None Fatal Asserton,continues execution.
    //FAIL(); // Fatal Assertion, does not continue execution of this test.
    EXPECT_TRUE(1==2)<<"1==2 failed!";      // To verify if the statements after failure are executed
    SUCCEED();
}

For exception checks:

Three types:

EXPECT_NO_THROW(statement); No exception thrown

EXPECT_ANY_THROW(statement); Any type of exception thrown

EXPECT_THROW(statement, exception_type); Specific type of exception thrown

TEST(ExceptionTest,et) {
    // No exception thrown
    EXPECT_ANY_THROW(10/0); //Expected: 10/0 throws an exception.
    // Specific type of exception thrown
    EXPECT_THROW(1/0,int); //Expected: 1/0 throws an exception of type int.
}

For Predicate Assertions:

When checking if a function returns TRUE or FALSE, it's helpful to output the parameters passed in, for better tracking after failure.

For example:

// Predicate Assertions
bool MN(int m, int n)
{
    return m>n;
}
// Checking if a function returns TRUE or FALSE, helpful to output the parameters passed in, for better tracking after failure
TEST(PredicateAssertionTest, Demo)
{
    int m = 5, n = 6;
    EXPECT_PRED2(MN, m, n); // Only provides <=5 parameters, parameters should correspond to function MN
}

EXPECT_PRED2(pred2, val1, val2); Similar to ASSERT. PRED2 can go up to PRED5.

Supports up to 5 parameters.

For floating point checks:

EXPECT_FLOAT_EQ(expected, actual) and EXPECT_DOUBLE_EQ(expected, actual)

Similar to ASSERT.

For comparing two closely-related numbers:

EXPECT_NEAR(val1, val2, abs_error);

Similar to ASSERT.

For type checks:

// Type checks
template <typename T>
class Tt{
public:
    void foo() {
        testing::StaticAssertTypeEq<bool, T>();     // No error if consistent with the following call, otherwise error
    }
};
TEST(TypeAssertionTest, Demo)
{
    Tt<bool> tt;
    tt.foo();
}

Learning reference: https://www.cnblogs.com/coderzh/archive/2009/04/06/1430364.html