DEV Community

Yanujz
Yanujz

Posted on

Getting started with GoogleTest and CMake

I wanted to get started with GoogleTest, and while searching for information, I came across this solution. I hope it can save you some time in your search.

Prerequisites

  • A compatible operating system (e.g. Linux, macOS, Windows).
  • A compatible C/C++ compiler
  • CMake installed

Set up the project

To get started with GoogleTest, follow these steps to set up your project structure. Below is the folder tree we'll use and a brief explanation of each component:

Folder tree

googletest-boilerplate              # Root directory of your project
├── CMakeLists.txt                  # Top-level CMake configuration file
├── src                             # Directory for source code
│   ├── CMakeLists.txt              # CMake configuration for source files
│   ├── example.c                   # C source file with implementation code
│   ├── example.h                   # Header file for example.c
│   └── main.c                      # Main source file
└── tests                           # Directory for test code
    ├── CMakeLists.txt              # CMake configuration for test files
    ├── test_example.cpp            # C++ file containing test cases
    └── test_main.cpp               # C++ file that sets up GoogleTest and runs tests

Enter fullscreen mode Exit fullscreen mode

Let’s start with the top-level CMakeLists.txt file. This file configures the overall project and includes the subdirectories for source and test files. Here’s what the content will look like:

cmake_minimum_required(VERSION 3.14)  # Minimum CMake version required
project(googletest-boilerplate VERSION 0.1.0 LANGUAGES C CXX)  # Project name and version, specifying C and C++ languages

# Set the C and C++ standards
set(CMAKE_C_STANDARD 11)  # Set C standard to C11
set(CMAKE_C_STANDARD_REQUIRED True)  # Ensure the C standard is required

set(CMAKE_CXX_STANDARD 14)  # Set C++ standard to C++14
set(CMAKE_CXX_STANDARD_REQUIRED True)  # Ensure the C++ standard is required

# Add subdirectories for source and test files
add_subdirectory(src)  # Includes the src directory
add_subdirectory(tests)  # Includes the tests directory

include(CTest)  # Include CTest module for testing support
Enter fullscreen mode Exit fullscreen mode

Followed by tests/CMakeLists.txt:

include(FetchContent)

# Fetch GoogleTest
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG        release-1.11.0
)
FetchContent_MakeAvailable(googletest)

# Collect  C++ source files recursively
file(GLOB_RECURSE CXX_FILES "${CMAKE_CURRENT_LIST_DIR}/*.cpp")
add_executable(unit_tests ${CXX_FILES})

# Link GoogleTest libraries
target_link_libraries(unit_tests
    PRIVATE
    gtest_main
    ${PROJECT_NAME}_lib  # Link to the main project library
)
# Include directories (including where GoogleTest is built)
target_include_directories(unit_tests PRIVATE ${gtest_SOURCE_DIR}/include)
# Add include directories for tests to find headers
target_include_directories(unit_tests PRIVATE ${PROJECT_SOURCE_DIR}/src)


# Enable testing and discover tests
# Discover and run tests
include(GoogleTest)
gtest_discover_tests(unit_tests)
Enter fullscreen mode Exit fullscreen mode

Followed by src/CMakeList.txt:

# Set the source directory to the current directory
set(SRC_DIR ${CMAKE_CURRENT_LIST_DIR})

# Recursively collect all .c and .h files in the source directory
file(GLOB_RECURSE SOURCES ${SRC_DIR}/*.c ${SRC_DIR}/*.h)

# Define the name of the main project library using the project name with a "_lib" suffix
set(MAIN_PROJECT_LIBNAME ${PROJECT_NAME}_lib)

# Create a static library from the collected source files
add_library(${MAIN_PROJECT_LIBNAME} STATIC
    ${SOURCES}
)

# Add the main executable, specifying 'main.c' as the source file
add_executable(${PROJECT_NAME} main.c)

# Link the main executable with the static library created earlier
target_link_libraries(${PROJECT_NAME} ${MAIN_PROJECT_LIBNAME})

Enter fullscreen mode Exit fullscreen mode

On the CMake side, we're done. Let's quickly take a look at the sources now.

tests/test_main.cpp

#include <gtest/gtest.h>  // Include the GoogleTest framework header

// Dummy test
// Define a test case named 'ExampleTest' and a test named 'test'
TEST(ExampleTest, test)
{
    EXPECT_EQ(true, true);
}

int main(int argc, char **argv) {
    // Initialize the GoogleTest framework with command-line arguments
    ::testing::InitGoogleTest(&argc, argv);

    // Run all the tests that have been defined and return the result
    return RUN_ALL_TESTS();
}
Enter fullscreen mode Exit fullscreen mode

Note: In the code below, we can access the functions from example.h because we included the src directory in the include path by specifying the following line in tests/CMakeLists.txt (line 24):

target_include_directories(unit_tests PRIVATE ${PROJECT_SOURCE_DIR}/src)
Enter fullscreen mode Exit fullscreen mode

If your project has a more complex directory structure, make sure to include the necessary directories in the include path to ensure proper access to header files.

tests/test_example.cpp
This will be the core test of our example. We'll call the functions inside the src directory."

#include <gtest/gtest.h>
#include "example.h"


TEST(Example, sumTest)
{
    uint32_t a = 10;
    uint32_t b = 20;

    EXPECT_EQ(a + b, sum(a, b));
}

TEST(Example, squareTest)
{
    uint32_t a = 10;

    EXPECT_EQ(a * a, square(a));
}

Enter fullscreen mode Exit fullscreen mode

Source code files here below:
src/example.h

#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif

uint32_t sum(uint32_t a, uint32_t b);

uint32_t square(uint32_t a);

#ifdef __cplusplus
}
#endif
Enter fullscreen mode Exit fullscreen mode

src/example.c

#include "example.h"

uint32_t sum(uint32_t a, uint32_t b)
{
    return a + b;
}

uint32_t square(uint32_t a)
{
    return a*a;
}
Enter fullscreen mode Exit fullscreen mode

src/main.c

#include <stdio.h>

int main(int argc, char** argv)
{
    puts("Hello World!");
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Compilation

To compile the project, follow these steps:

mkdir build
cd build
cmake ..
cmake --build .
Enter fullscreen mode Exit fullscreen mode

Run the test suite

After compiling, you can run the test suite using the following command:

./tests/unit_tests
Enter fullscreen mode Exit fullscreen mode

Upon running the tests, you should see output similar to this:

[==========] Running 3 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 2 tests from Example
[ RUN      ] Example.sumTest
[       OK ] Example.sumTest (0 ms)
[ RUN      ] Example.squareTest
[       OK ] Example.squareTest (0 ms)
[----------] 2 tests from Example (0 ms total)

[----------] 1 test from ExampleTest
[ RUN      ] ExampleTest.test
[       OK ] ExampleTest.test (0 ms)
[----------] 1 test from ExampleTest (0 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 2 test suites ran. (0 ms total)
[  PASSED  ] 3 tests.
Enter fullscreen mode Exit fullscreen mode

If you see a similar output, congratulations! Your setup is complete and the tests have passed successfully.

GitHub Repository

You can find the full project and source code on GitHub:

Source code

Feel free to clone the repository, explore the code, and contribute if you have any improvements or suggestions.

I hope this article has saved you some time searching for this information online.

Happy coding!

Top comments (1)

Collapse
 
pgradot profile image
Pierre Gradot • Edited

Since you have include(CTest), I guess you can simply use the command ctest to run the tests. Otherwise, this line is probably not necessary.

Note that using file(GLOB_RECURSE.... to get the source file of your project is generally considered as a bad practice, because it has several drawbacks. Instead, simply manually list your files by name. It's generally used when you can't manually enumerate the source files (eg: when files are generated and we can't predict their name easily).