Using Google Test Library for Unit Testing with CMake

Using Google Test Library for Unit Testing with CMakeUsing Google Test Library for Unit Testing with CMake

Introduction:

In this article, we will learn how to implement unit testing using the CMake build system with the Google Test framework. Compared to the previous configuration with Catch2, the Google Test framework is not just a header file; it is a library that includes two files that need to be built and linked. We can place them together with our code project, but to keep the project lightweight, we will choose to download a well-defined Google Test during configuration, then build the framework and link it. We will use the newer FetchContent module (available from CMake version 3.11). Related usage will be covered in subsequent notes.

Additionally, we will select a testing framework (currently Google Test is more popular) after completing the relevant tests and write a small project to practice and familiarize ourselves with as many features of the framework as possible.

Using Google Test Library for Unit Testing with CMake

Project Structure

NOTE

main.cpp, sum_integers.cpp, and sum_integers.hpp remain the same as in the previous content, and we will make relevant modifications to test.cpp.

.
├── CMakeLists.txt
├── main.cpp
├── sum_integers.cpp
├── sum_integers.h
└── test.cpp

Project address:

https://gitee.com/jiangli01/tutorials/tree/master/cmake-tutorial/chapter4/03

CMakeLists.txt

cmake_minimum_required(VERSION 3.11 FATAL_ERROR)

project(test_gtest LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

add_library(sum_integers sum_integers.cpp)

add_executable(sum_up main.cpp)
target_link_libraries(sum_up sum_integers)

option(ENABLE_UNIT_TESTS "Enable unit tests" ON)
message(STATUS "Enable testing: ${ENABLE_UNIT_TESTS}")

if(ENABLE_UNIT_TESTS)
  include(FetchContent)

  FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://gitcode.net/mirrors/google/googletest.git
    GIT_TAG        release-1.8.0
  )

  FetchContent_GetProperties(googletest)

  if(NOT googletest_POPULATED)
    FetchContent_Populate(googletest)

    # Prevent GoogleTest from overriding our compiler/linker options
    # when building with Visual Studio
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
    # Prevent GoogleTest from using PThreads
    set(gtest_disable_pthreads ON CACHE BOOL "" FORCE)

    # adds the targets: gtest, gtest_main, gmock, gmock_main
    add_subdirectory(
      ${googletest_SOURCE_DIR}
      ${googletest_BINARY_DIR}
    )

    # Silence std::tr1 warning on MSVC
    if(MSVC)
      foreach(_tgt gtest gtest_main gmock gmock_main)
        target_compile_definitions(${_tgt}
          PRIVATE
            "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING"
        )
      endforeach()
    endif()
  endif()

  add_executable(cpp_test "")

  target_sources(cpp_test
    PRIVATE
      test.cpp
  )

  target_link_libraries(cpp_test
    PRIVATE
      sum_integers
      gtest_main
  )

  enable_testing()

  add_test(
    NAME google_test
    COMMAND $<TARGET_FILE:cpp_test>
  )
endif()

Note: The FetchContent module can only be used from CMake version 3.11 onwards.

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

This tells CMake to automatically export all symbols (functions, variables, classes, etc.) from shared libraries (DLL) on the Windows platform.

When this option is set to ON, CMake will automatically insert export directives in the library’s code to ensure they can be linked externally. This is particularly important on Windows as explicit export declarations are required for symbols to be accessed from outside the DLL.

However, enabling this option may lead to larger binary files and may inadvertently expose symbols. If multiple libraries are linked, it may also cause symbol conflicts. Therefore, while it simplifies symbol export, careful consideration of its impact and whether it is suitable for your project is necessary.

option(ENABLE_UNIT_TESTS "Enable unit tests" ON)
message(STATUS "Enable testing: ${ENABLE_UNIT_TESTS}")
if(ENABLE_UNIT_TESTS)
    # all the remaining CMake code will be placed here
endif()

Check ENABLE_UNIT_TESTS. By default, it is set to ON, but sometimes it needs to be set to OFF to allow the use of Google Test without a network connection.

include(FetchContent)

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://gitcode.net/mirrors/google/googletest.git
  GIT_TAG        release-1.8.0
)

The FetchContent module is used to download and integrate the Google Test library (googletest).

  • include(FetchContent): Used to include the FetchContent module, which allows fetching external dependencies in the project.

  • FetchContent_Declare(googletest ...): Uses the FetchContent_Declare macro to declare the external dependency to be fetched. In this case, it declares an external dependency named googletest.

  • GIT_REPOSITORY: This specifies the URL of the Git repository used to fetch the library.

  • GIT_TAG: This is a specific tag or branch in the Git repository that specifies the version of the library to be fetched. Here, it specifies the version of the Google Test library as release-1.8.0.

With this code, when building the project, CMake will attempt to download and integrate the Google Test library so that unit testing can be performed in the project. Note that in actual projects, it may also be necessary to link the Google Test library in the test targets and set up test cases, etc.

if(NOT googletest_POPULATED)
    FetchContent_Populate(googletest)

    # Prevent GoogleTest from overriding our compiler/linker options
    # when building with Visual Studio
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
    # Prevent GoogleTest from using PThreads
    set(gtest_disable_pthreads ON CACHE BOOL "" FORCE)

    # adds the targets: gtest, gtest_main, gmock, gmock_main
    add_subdirectory(
      ${googletest_SOURCE_DIR}
      ${googletest_BINARY_DIR}
    )

    # Silence std::tr1 warning on MSVC
    if(MSVC)
      foreach(_tgt gtest gtest_main gmock gmock_main)
        target_compile_definitions(${_tgt}
          PRIVATE
            "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING"
        )
      endforeach()
    endif()
  endif()
  • if(NOT googletest_POPULATED): Checks if the Google Test library has already been fetched and integrated. If googletest_POPULATED is not defined, the code block will be executed.

  • FetchContent_Populate(googletest): Fills the downloaded Google Test library content into the specified directory for subsequent building and integration.

  • set(gtest_force_shared_crt ON CACHE BOOL "" FORCE): Forces Google Test to use the shared runtime (C Runtime) library when building with Visual Studio. This can avoid linking errors during the build.

  • set(gtest_disable_pthreads ON CACHE BOOL "" FORCE): Disables Google Test‘s use of pthreads. This may be necessary in some environments, such as platforms without pthreads support.

  • add_subdirectory(...): Adds the Google Test library to the project. It will be built in the specified source and binary directories.

  • For Microsoft Visual Studio (MSVC), during the build of the Google Test library, macro definitions are added to the gtest, gtest_main, gmock, and gmock_main targets to eliminate warnings under MSVC.

Related Source Code

test.cpp

#include "sum_integers.h"
#include "gtest/gtest.h"
#include <vector>

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

TEST(example, sum_zero) {
  auto integers = {1, -1, 2, -2, 3, -3};
  auto result = sum_integers(integers);
  ASSERT_EQ(result, 0);
}

TEST(example, sum_five) {
  auto integers = {1, 2, 3, 4, 5};
  auto result = sum_integers(integers);
  ASSERT_EQ(result, 15);
}

Output

mkdir build
cd build
cmake ..
ctest
Test project /home/jiangli/repo/tutorials/cmake-tutorial/chapter4/03/build
    Start 1: google_test
1/1 Test #1: google_test ......................   Passed    0.00 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) =   0.01 sec
./cpp_test
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from example
[ RUN      ] example.sum_zero
[       OK ] example.sum_zero (0 ms)
[ RUN      ] example.sum_five
[       OK ] example.sum_five (0 ms)
[----------] 2 tests from example (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 2 tests.

Finally, I wish everyone to become stronger!!!

Using Google Test Library for Unit Testing with CMakeUsing Google Test Library for Unit Testing with CMake

Leave a Comment