DEV Community

Yanujz
Yanujz

Posted on

2

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!

Billboard image

Imagine monitoring that's actually built for developers

Join Vercel, CrowdStrike, and thousands of other teams that trust Checkly to streamline monitor creation and configuration with Monitoring as Code.

Start Monitoring

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).

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay