C++ Unit Testing

Unit testing is an essential part of ensuring the reliability of your C++ code. The goal is to test individual components or units of code in isolation to catch bugs early. In C++, the most popular framework for unit testing is Google Test (gtest). However, other frameworks like Catch2 and Boost.Test are also widely used.

🔹 1. Setting Up Google Test (gtest)

Step 1: Install Google Test

You can easily install Google Test via package managers or build it from source.

On Ubuntu:

sudo apt-get install libgtest-dev
sudo apt-get install cmake
cd /usr/src/gtest
sudo cmake .
sudo make
sudo cp libgtest*.a /usr/lib

With CMake (preferred approach):

git clone https://github.com/google/googletest.git
cd googletest
mkdir build
cd build
cmake ..
make
sudo make install

Step 2: Basic Google Test Example

Here’s a simple example demonstrating how to set up a unit test using Google Test.

File: math_operations.cpp

#include <cmath>

// Function to calculate the square root of a number
double sqrt_func(double num) {
    return sqrt(num);
}

File: test_math_operations.cpp

#include <gtest/gtest.h>
#include "math_operations.cpp"

// Test case for the sqrt_func function
TEST(MathOperationsTest, PositiveNumber) {
    ASSERT_DOUBLE_EQ(sqrt_func(4.0), 2.0);
}

TEST(MathOperationsTest, Zero) {
    ASSERT_DOUBLE_EQ(sqrt_func(0.0), 0.0);
}

TEST(MathOperationsTest, NegativeNumber) {
    ASSERT_TRUE(std::isnan(sqrt_func(-1.0)));
}

// Main function to run all tests
int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Step 3: Build and Run the Test

Use CMake to build the test.

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(MathOperationsTest)

# Enable testing
enable_testing()

# Add GoogleTest
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})

# Create the executable
add_executable(runTests test_math_operations.cpp)

# Link GoogleTest libraries
target_link_libraries(runTests ${GTEST_LIBRARIES} pthread)

Commands to compile and run the test:

mkdir build
cd build
cmake ..
make
./runTests

🔹 2. Testing Basics with Google Test

Assertions

In Google Test, assertions are used to verify that the program behaves as expected.

  • ASSERT_EQ(val1, val2): Asserts that val1 == val2. If the condition is false, the test fails and terminates.
  • ASSERT_NE(val1, val2): Asserts that val1 != val2.
  • ASSERT_LT(val1, val2): Asserts that val1 < val2.
  • ASSERT_TRUE(val): Asserts that val is true.
  • ASSERT_FALSE(val): Asserts that val is false.
  • ASSERT_DOUBLE_EQ(val1, val2): Asserts that two floating-point numbers are equal within a tolerance.

Test Fixtures

If your tests require a common setup (e.g., creating objects or initializing data), you can use test fixtures.

#include <gtest/gtest.h>

class MathOperationsTest : public ::testing::Test {
protected:
// Setup code that runs before each test
void SetUp() override {
num1 = 4.0;
num2 = 9.0;
}

double num1;
double num2;
};

TEST_F(MathOperationsTest, TestSqrt) {
ASSERT_DOUBLE_EQ(sqrt_func(num1), 2.0);
ASSERT_DOUBLE_EQ(sqrt_func(num2), 3.0);
}

TEST_F is used to create tests that work with a test fixture class.


🔹 3. Running Tests & Test Results

Running Specific Tests

You can run specific tests or groups of tests using the --gtest_filter flag.

  • To run all tests:./runTests
  • To run specific tests: ./runTests --gtest_filter=MathOperationsTest.PositiveNumber

Test Results

When you run the tests, Google Test will output results in the terminal:

bashCopyEdit[ RUN      ] MathOperationsTest.PositiveNumber
[       OK ] MathOperationsTest.PositiveNumber (0 ms)
[ RUN      ] MathOperationsTest.Zero
[       OK ] MathOperationsTest.Zero (0 ms)
[ RUN      ] MathOperationsTest.NegativeNumber
[       OK ] MathOperationsTest.NegativeNumber (0 ms)
[----------] 3 tests from MathOperationsTest (1 ms total)

🔹 4. Best Practices for Unit Testing in C++

  • Test Small Units of Code: Each test should focus on testing a single function or method.
  • Write Clear Test Cases: The name of the test case should describe what is being tested. For example, TestAdditionWithNegativeNumbers is better than TestAddition.
  • Use Mocks and Stubs: For testing components in isolation, you can use mock objects to simulate external dependencies (e.g., databases, networks).
  • Test Edge Cases: Make sure to test edge cases like empty inputs, boundary values, and invalid inputs.
  • Automate Your Tests: Use CI/CD pipelines to run tests automatically.

🔹 5. Other C++ Unit Testing Frameworks

While Google Test is the most popular, there are other frameworks that might suit your needs:

  1. Catch2
  2. Boost.Test
  3. CppUnit
    • Based on the JUnit style, C++ version of the famous Java testing framework.
  4. Doctest

🔹 6. Conclusion

Unit testing helps you ensure that your C++ code is robust and performs as expected. Google Test is widely used because of its simplicity and rich feature set. While you should be using assertions to check expected behavior, also consider using test fixtures for common setup code and mocking to isolate dependencies.

By incorporating unit testing into your development cycle, you can catch bugs earlier, improve code quality, and make future changes more easily without breaking existing functionality.

Let me know if you need specific help with setting up tests or with any particular C++ unit testing concepts!