C++ Testing Patterns
Google Test Basics
#include <gtest/gtest.h>
// Simple test
TEST(MathTest, AddPositives) {
EXPECT_EQ(add(2, 3), 5);
EXPECT_NE(add(2, 3), 6);
EXPECT_LT(add(-1, 0), 1);
}
// Fixture — shared setup/teardown
class DatabaseTest : public ::testing::Test {
protected:
void SetUp() override {
db_ = std::make_unique<Database>(":memory:");
db_->exec("CREATE TABLE users (id INT, name TEXT)");
}
void TearDown() override { db_.reset(); }
std::unique_ptr<Database> db_;
};
TEST_F(DatabaseTest, InsertAndFind) {
db_->exec("INSERT INTO users VALUES (1, 'Alice')");
auto user = db_->findUser(1);
ASSERT_TRUE(user.has_value());
EXPECT_EQ(user->name, "Alice");
}
Parameterized Tests
class PrimeTest : public ::testing::TestWithParam<int> {};
TEST_P(PrimeTest, IsPrime) {
EXPECT_TRUE(isPrime(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(
KnownPrimes, PrimeTest,
::testing::Values(2, 3, 5, 7, 11, 13, 17)
);
// Typed test (same test, multiple types)
template<typename T>
class NumericTest : public ::testing::Test {};
using NumericTypes = ::testing::Types<int, long, float, double>;
TYPED_TEST_SUITE(NumericTest, NumericTypes);
TYPED_TEST(NumericTest, ZeroIsNeutral) {
EXPECT_EQ(TypeParam(0) + TypeParam(5), TypeParam(5));
}
GMock
#include <gmock/gmock.h>
class IStorage {
public:
virtual ~IStorage() = default;
virtual bool save(std::string_view key, std::string_view value) = 0;
virtual std::optional<std::string> load(std::string_view key) = 0;
};
class MockStorage : public IStorage {
public:
MOCK_METHOD(bool, save, (std::string_view, std::string_view), (override));
MOCK_METHOD(std::optional<std::string>, load, (std::string_view), (override));
};
TEST_F(CacheTest, SavesOnMiss) {
MockStorage storage;
EXPECT_CALL(storage, load("key")).WillOnce(::testing::Return(std::nullopt));
EXPECT_CALL(storage, save("key", "value")).WillOnce(::testing::Return(true));
Cache cache(storage);
cache.get("key", [] { return "value"; });
}
Catch2
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
TEST_CASE("Parser handles valid JSON", "[parser]") {
SECTION("parses integer") {
auto val = parse("42");
REQUIRE(val.is_int());
CHECK(val.as_int() == 42);
}
SECTION("parses array") {
auto val = parse("[1,2,3]");
REQUIRE(val.is_array());
CHECK_THAT(val.as_vector(), Catch::Matchers::Equals(std::vector{1,2,3}));
}
}
TEST_CASE("Parser throws on invalid input", "[parser]") {
REQUIRE_THROWS_AS(parse("{invalid"), ParseError);
}
Google Benchmark
#include <benchmark/benchmark.h>
static void BM_StringSearch(benchmark::State& state) {
std::string haystack(state.range(0), 'x');
haystack += "needle";
for (auto _ : state) {
auto pos = haystack.find("needle");
benchmark::DoNotOptimize(pos);
}
state.SetBytesProcessed(state.iterations() * state.range(0));
}
BENCHMARK(BM_StringSearch)->Range(8, 8 << 20);
BENCHMARK_MAIN();
Assertions Quick Reference
// Fatal (stops test function on failure)
ASSERT_EQ(a, b); ASSERT_NE(a, b);
ASSERT_LT(a, b); ASSERT_LE(a, b);
ASSERT_GT(a, b); ASSERT_GE(a, b);
ASSERT_TRUE(expr); ASSERT_FALSE(expr);
ASSERT_STREQ(s1, s2); // C-strings
ASSERT_THROW(expr, ExType);
ASSERT_NO_THROW(expr);
// Non-fatal (continues test)
EXPECT_EQ / EXPECT_NE / EXPECT_FLOAT_EQ / EXPECT_NEAR