Skip to content

Integrating Google Test Into CMake Projects

During software development, it’s important to be able to test your code, so you can catch those pesky bugs as early as possible in the development process. For C++ software engineers, this means having many unit and functional tests that can tell you how your code behaves in certain situations. Unsurprisingly, Google Test is the most popular testing framework for these purposes, and it can be easily integrated with CMake.

Animation of building a CMake project that includes Google Test, then showing google test output.
Figure 1: Building a GTest project and running the tests.

If you haven’t yet come across Google Test, it simply allows you to define test cases with its API. In addition, it creates a very light-weight executable/program with beautiful pass/failure outputs based on your test outcomes. Perfect for CI/CD pipeline integration.

What’s even more interesting? Google Tests can be integrated quite easily into your CMake projects, using CMake’s test tool: CTest.

Contents Of This Post

Getting started with Google Tests in a CMake project is very easy. In this post, we look at three different ways to achieve this:

Hopefully, one of the options above will persuade you to use Google Tests in your projects. Believe it or not, I’ve seen many projects (commercial and open-source) that don’t, and they end up wasting a lot of time writing their own flawed testing frameworks!

Fitting Google Test Into A CMake Typical Project – The Big Picture

Firstly, let’s look at an example of a typical C++ project. For example, say your project defines a library with some common code and an executable that uses the functions inside the library. Unsurprisingly, we will look at a very similar setup in this post.

Specifically, we consider a library that defines three different functions. Particularly, we create a target called multiply, which defined three functions to multiply two numbers. Essentially, a binding for each type {int, float, double}.

File Structure Of The Test Project

In the textbox below, you can find the directory structure for the basic example we’re considering in this post.

.
├── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   └── multiply
│       ├── CMakeLists.txt
│       ├── include
│       │   └── multiply
│       │       └── multiply.h
│       └── multiply.cpp
└── tests
    ├── CMakeLists.txt
    └── multiply_test.cpp

Moreover, you can find the contents of important files in the following subsections. However, we basically have all the code for the library under src/multiply, and the test executable under tests. Additionally, each directory has a CMakeLists.txt, either to include the subdirectories or to define targets in the current directory.

Main CMakeLists.txt – Defining The Project And Adding Subdirectories

As you can see below, the main CMakeLists.txt file declares the project properties and CMake’s boilerplate code.

cmake_minimum_required(VERSION 3.16)
project(MultiplyTest LANGUAGES CXX)

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

enable_testing()

add_subdirectory(src)
add_subdirectory(tests)

Clearly, we’re setting the minimum version of CMake, declaring the project name, and immediately settings the C++ standard version boilerplate code. Interestingly, we need the call to enable_testing() as it tells CMake to generate the tests that we will later on create. Remember to add enable_testing() in your root CMakeLists.txt, before any tests are created.

Lastly, we add the subdirectories for the library source code and the test code.

Creating A Dummy CMake Library For Our Google Tests

Under the directory src, we define the code and CMake targets for our dummy library multiply. Specifically, the interesting bits will be under the multiply directory, so the file src/CMakeLists.txt only has a call to add_subdirectory(multiply) in it.

For this reason, the filesrc/multiply/CMakeLists.txt defines our library multiply, and the include directories for it, as you can see below.

add_library(multiply multiply.cpp)

target_include_directories(multiply PUBLIC include)

With the above CMake structure in mind for our dummy target, the source file multiply.cpp has the definitions of the functions for our multiply library.

#include <multiply/multiply.h>

int multiply(int a, int b)
{
    return a*b;
}

float multiply(float a, float b)
{
    return a*b;
}

double multiply(double a, double b)
{
    return a*b;
}

Complementary, you can find the contents of the header file src/multiply/include/multiply/multiply.h below.

int multiply(int a, int b);

float multiply(float a, float b);

double multiply(double a, double b);

Defining The Google Tests

Having created our dummy library with a few public functions, we now need to test it. Luckily for you, I created an executable with a few unit tests. Check the content of the relevant C++ source file below.

#include <multiply/multiply.h>
#include <gtest/gtest.h>

TEST(MultiplyTests, TestIntegerOne_One)
{
    const auto expected = 1;
    const auto actual = multiply(1, 1);
    ASSERT_EQ(expected, actual);
}

TEST(MultiplyTests, TestIntegerZero_Zero)
{
    const auto expected = 0;
    const auto actual = multiply(0, 0);
    ASSERT_EQ(expected, actual);
}

TEST(MultiplyTests, TestIntegerZero_One)
{
    const auto expected = 0;
    const auto actual = multiply(0, 1);
    ASSERT_EQ(actual, expected);
}

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

Finally, to complete the setup, we define the CMake test targets in the file tests/CMakeLists.txt below.

# GTest include code would
# go somewhere in here

add_executable(multiply_test multiply_test.cpp)

target_link_libraries(multiply_test
 PRIVATE
  GTest::GTest
  multiply)

add_test(multiply_gtests multiply_test)

However, not that I left a comment block at the top of the CMake file above. Due to having different ways of integrating Google Tests into your project, this CMakeLists.txt will change. Hopefully, you should quickly see this in the upcoming sections about the actual integration!

Learn More About The CMake Techniques Used So Far

Before we move on to the next sections, I want to make sure you’re not stuck or wondering what we’ve just done!

Specifically, we made heavy use of CMake to define a C++ project, libraries, and executables. For more information, check out the post on how to use CMake’s add_library(...) for creating libraries, and also how to use CMake’s include_directories(...) to expose public functions/API for a library!

Integrating Google Tests With CMake’s fetch_content(...)

Unsurprisingly, there’s a way to include third-party libraries purely with CMake. Using the module “FetchContent”, we can use the functions FetchContent_Declare(...) and FetchContent_MakeAvailable(...) that pretty much copies or downloads a different CMake project and makes it part of your build.

Without further ado, the code block below is the modified tests/CMakeLists.txt that pulls version 1.11.0 of GTest, then links it to our multiply_test executable.

include(FetchContent)

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        release-1.11.0
)
FetchContent_MakeAvailable(googletest)
add_library(GTest::GTest INTERFACE IMPORTED)
target_link_libraries(GTest::GTest INTERFACE gtest_main)

add_executable(multiply_test multiply_test.cpp)

target_link_libraries(multiply_test
 PRIVATE
  GTest::GTest
  multiply)

add_test(multiply_gtests multiply_test)

Long story short, the FetchContent_* calls above will pull the code from Google Tests’ git repository, then include it as part of your build, hence all the targets defined in Google Tests’ CMake will also be available in our build.

In addition, I added the calls to add_library(GTest::GTest INTERFACE IMPORTED) and target_link_libraries(GTest::GTest INTERFACE gtest_main) to make the “linking” of GTests more consistent with the other methods.

Building The Tests With The FetchContent Method

Due to the FetchContent method being a pure CMake way of integrating third-party libraries, this is perhaps the easiest one to build. For this reason, the short code block below is enough to build the entire thing.

# Assuming you're executing these commands
# from the root directory!
mkdir build && cd build
cmake ..
cmake --build .

If all goes well, you should have the test executable under build/tests/multiply_tests, and the library files under the directorybuild/src/multiply/.

Furthermore, since we added a CMake test with add_test(...) in tests/CMakeLists.txt, you should be able to run all defined tests by executing the command ctest under the build/ directory.

My Thoughts And Further Information On FetchContent

For simple projects with few dependencies, FetchContent is an acceptable way to integrate third-party dependencies defined with CMake. Thankfully, the CMake documentation on FetchContent isn’t too bad, so you can read more about it there.

Although CMake’s FetchContent does handle non-CMake projects, it’s way harder to include these third-party dependencies. More precisely, you would find yourself writing a lot of extra CMake to CMake-fy your third-party dependency, essentially maintaining it for your build system. We won’t get into how to do that in this post.

In terms of versioning, FetchContent actually pulls Google Tests with the version given in the GIT_TAG parameter. I would stick to git tags or commit hashes, but it also supports branch names.

For a comparison between FetchContent and the other methods, refer to the summary section of this post!

Conan To Manage Google Tests As A Third-Party Dependency

Conan is a package manager for C++. In simple terms, this means that Conan is a tool that helps you include third-party packages into your build. Unfortunately, we will not explain how Conan works in depth, however, the list below outlines the main steps for including Google Tests with Conan.

  • Add all the third-party dependencies of your project inside the file conanfile.txt.
  • Install the dependencies with Conan, preferably inside your build directory.
  • Modify your CMake to include the dependencies downloaded with Conan.
  • Build your project.

Understandably, doing all of this may seem like a lot. However, it all boils down to creating a text file and adding a few lines to the already existing tests/CMakeLists.txt.

And of course, make sure you install Conan by following the official guide!

Declaring And Downloading Dependencies With Conan

As previously mentioned, we need to create the file conanfile.txt with the dependencies for our C++ build. In our case, we simply need a dependency on Google Tests version 1.11.0, as you can see below.

[requires]
gtest/1.11.0

[generators]
cmake_find_package

Basically, the conanfile.txt above says we want to use the gtests/1.11.0 package in our project, and we want Conan to generate CMake find packages, so we can use it with find_package(...) in CMake. For more information on the conanfile.txt, check out Conan’s documentation. To find out which libraries and versions are available, check out the Conan center website.

Following the creation of the file above in your project’s root directory, you need to ask Conan to install the dependencies in your machine with the following commands.

mkdir -p build && cd build
conan install .. \
  -scompiler=gcc \
  -scompiler.version=9 \
  -scompiler.libcxx=libstdc++11 \
  --build=missing

Expectedly, the commands above download all the source code for the dependencies and will build them. Furthermore, note that I defined a few settings for my build, such as the compiler I’m using, the version of the compiler, and the C++ standard library implementation to use libstdc++11. Feel free to match your compiler name and version, but you must use that C++ standard library for Google Tests to compile smoothly.

After these steps, you will find a FindGTest.cmake file under the build/ directory. Hence, it means that everything worked well.

Modifying The CMake And Building The GTest Executable

Luckily, we only need to make one minor modification to the file tests/CMakeLists.txt, as you can see below.

find_package(GTest REQUIRED)

add_executable(multiply_test multiply_test.cpp)

target_link_libraries(multiply_test
 PRIVATE
  GTest::GTest
  multiply)

Essentially, we added the call to find_pacakge(GTest REQUIRED). Long story short, this will use the FindGTest.cmake file to define the target GTest::GTest. Finally, the code block below shows the commands to build the project from the command line.

cd build
cmake -DCMAKE_MODULE_PATH="$(pwd)" ..
cmake --build .

And that’s it, you should have compiled everything, and you can run all the tests with ctest under the build directory.

Before we finish this section, I want to point out that the option -DCMAKE_MODULE_PATH="$(pwd)" will essentially tell CMake where it can find the FingGTest.cmake file. That’s why the find_package(GTest REQUIRED) command finds GTest!

Vcpkg As Another Third-Party Way Of Getting Google Tests Into Your Project

Vcpkg is also a C++ package manager. Similarly, we also need to declare the dependencies and install them with Vcpkg, before building with CMake. You can find the summary of the steps needed to include Google Tests with Vcpkg in the list below.

  • Declare the Google Test dependency by writing the file vcpkg.json.
  • Install the dependencies with Vcpkg.
  • Modify CMake to link the GTest target.
  • Build the project.

Unsurprisingly, we assume you have installed Vcpkg. If not, you can follow the official getting started to install Vcpkg. In addition, you might want to make sure that you can invoke the executable vcpkg from your terminal. For example, I had to add the following line below to my .bashrc.

alias vcpkg="/home/matheus/vcpkg/vcpkg"

In my case, /home/matheus/vcpkg is the installation directory for vcpkg.

Declaring The Google Test Dependency With Vcpkg

Similar to Conan’s conanfile.txt, we declare our third-party dependencies inside the vcpkg.json file. Unlike Conan, Vcpkg handles the dependency versions a little differently, so vcpkg.json is a little more cumbersome, as you can see below.

{
    "name": "multiply-tests",
    "version": "0.0.0",
    "builtin-baseline": "43235cf746cddb4981a8ef4abdb80aafe4d4e0e2",
    "dependencies": [
        {
            "name": "gtest",
            "version>=": "1.11.0"
        }
    ]
}

As you can see, vcpkg.json has a little more information than the Conan counterpart. More specifically, it follows a certain JSON schema that accepts certain options. You can find out more about it in the Vcpkg manifest documentation.

For the sake of clearing things up, I explain some of the information in the file vcpkg.json below.

  • name is the name of your package/project.
  • version defines the version of your package. Since we’re not exporting this project, you can add any semantic version here.
  • builtin-baseline is a commit into the Vcpkg repository. This represents the baseline “snapshot” of Vcpkg’s packages, meaning that the versions of the dependencies installed will be at least what is available in this commit.
  • dependencies should declare an array of third-party dependencies your project needs. Each dependency is a JSON object with name and, optionally, a least version.

In our case, we only have one dependency: Google Tests version 1.11.0! For more information on packages that are available for Vcpkg, check out the packages browser for Vcpkg. Understandably, versioning with Vcpkg is very confusing, so I’d recommend checking out this Vcpkg versioning article by Microsoft.

Installing The Google Test Dependency With Vcpkg

Now that we declared the dependency on Google Tests, we need to ask Vcpkg to pull the code and build it locally, so we can include it in our project. Luckily for you, it’s a very simple command, as you can see in the box below.

# need to run this from the root dir
vcpkg install --x-install-root="build"

Unsurprisingly, the command above will use Vcpkg to download and build Google Test, and the installation files will be located under the build/vcpkgand build/x64-linuxdirectories. Unlike Conan, Vcpkg creates “config” files that are invoked when you use find_package(...) , so it’s basically a different mode of the same CMake command. I’d recommend looking at the CMake documentation if you’re interested in finding out how it works.

Modifying The CMake And Building The Google Tests

Assuming that the Vcpkg installation went smoothly, you should be able to just modify the tests CMakeLists.txt and build the project. In fact, we end up with the same tests/CMakeLists.txtas our Conan counterpart, both Conan and Vcpkg use the find_package(...) mechanism.

find_package(GTest REQUIRED)

add_executable(multiply_test multiply_test.cpp)

target_link_libraries(multiply_test
 PRIVATE
  GTest::GTest
  multiply)

add_test(multiply_gtests multiply_test)

Finally, the only thing remaining is configuring the project and building with CMake. The code box below shows the remaining steps.

# Run from project root
cd build
cmake .. \
  -DCMAKE_TOOLCHAIN_FILE="Path-To/vcpkg.cmake"

cmake --build .

Essentially, the only unusual parameter we pass to CMake is -DCMAKE_TOOLCHAIN_FILE="PATH-TO/vcpkg.cmake". This value points to the integration toolchain file installed with Vcpkg. Usually, this is located at PATH-TO-VCPKG-INSTALL/scripts/buildsystems/vcpkg.cmake. In case you cannot find this file, I recommend reading the Vcpkg guide on installing packages, as they describe where you can expect this file.

After this, you should have everything built under the build directory. Moreover, you can run all the tests by invoking ctest from the build directory.

Get The Full Code Examples

If you struggled to get the full picture in this post or cannot yet manually write and build all the code in this post, feel free to clone/download the full example in the Github repository.

Once you clone the repository, there are three directories:

  • conan is where you can find the Conan-based examples.
  • fetch-content has all the CMake-only project.
  • vcpkg contains everything you need to build the examples with Vcpkg.

In addition, each of the directories above has a build script you can run from the project’s root. For example, if you want to build the Conan example, use cd conan, followed by ./scripts/build.sh, and all the build files will be located under conan/build.

Which Method Should You Choose To Integrate Google Test Into CMake?

In this post, we looked at three different ways to include GTest into your CMake project. On one hand, we had a “complete” CMake method with FetchContent. On the other hand, we looked at including Google Tests with package managers.

If your project is very simple, and you have no more third-party dependencies aside from GoogleTest, then the FetchContent way is perfectly acceptable. With this purely CMake method, you don’t need to install further tools to integrate GTest.

However, beware that FetchContent only works well for other CMake dependencies, and it’s a lot harder to integrate non-CMake packages with it. In addition, the pulled dependencies are essentially copied and pasted into your build! Meaning that you may encounter target name clashes or other interferences with CMake.

If you have many dependencies and need to worry about versioning, you should probably choose a package manager for C++. As you’ve probably guessed, Conan and Vcpkg are the most popular package managers for C++. If you’re interested in learning more, check out my post on the differences between Conan and Vcpkg.


Have I missed anything? Do you have any questions/feedback regarding the post? Noticed any typos? Feel free to comment below and I’ll reply as soon as I can!

Published inCMakeCPP

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *