cpp
examples
examples.cpp⚙️cpp
/**
* Unit Testing Examples with Google Test
*
* To compile with Google Test:
* 1. Install gtest: sudo apt install libgtest-dev
* 2. Compile: g++ -std=c++17 examples.cpp -lgtest -lgtest_main -pthread -o test_examples
* 3. Run: ./test_examples
*
* Or use CMake (recommended - see CMakeLists.txt example)
*/
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <stdexcept>
#include <memory>
#include <algorithm>
// Uncomment when Google Test is available:
// #include <gtest/gtest.h>
using std::cout;
using std::endl;
// ============================================================================
// CODE UNDER TEST - Calculator Class
// ============================================================================
class Calculator {
public:
double add(double a, double b) const { return a + b; }
double subtract(double a, double b) const { return a - b; }
double multiply(double a, double b) const { return a * b; }
double divide(double a, double b) const {
if (b == 0) {
throw std::invalid_argument("Division by zero");
}
return a / b;
}
double power(double base, int exp) const {
return std::pow(base, exp);
}
double sqrt(double x) const {
if (x < 0) {
throw std::domain_error("Square root of negative number");
}
return std::sqrt(x);
}
// Memory functions
void memoryStore(double value) { memory_ = value; }
double memoryRecall() const { return memory_; }
void memoryClear() { memory_ = 0; }
private:
double memory_ = 0;
};
// ============================================================================
// EXAMPLE 1: Basic Tests
// ============================================================================
/*
TEST(CalculatorTest, AddsTwoPositiveNumbers) {
Calculator calc;
EXPECT_DOUBLE_EQ(calc.add(2.0, 3.0), 5.0);
}
TEST(CalculatorTest, AddsPositiveAndNegative) {
Calculator calc;
EXPECT_DOUBLE_EQ(calc.add(5.0, -3.0), 2.0);
}
TEST(CalculatorTest, AddsTwoNegativeNumbers) {
Calculator calc;
EXPECT_DOUBLE_EQ(calc.add(-2.0, -3.0), -5.0);
}
TEST(CalculatorTest, AddsWithZero) {
Calculator calc;
EXPECT_DOUBLE_EQ(calc.add(5.0, 0.0), 5.0);
EXPECT_DOUBLE_EQ(calc.add(0.0, 5.0), 5.0);
EXPECT_DOUBLE_EQ(calc.add(0.0, 0.0), 0.0);
}
*/
// ============================================================================
// EXAMPLE 2: Test Fixture
// ============================================================================
/*
class CalculatorFixtureTest : public ::testing::Test {
protected:
Calculator calc;
void SetUp() override {
// Reset calculator state before each test
calc.memoryClear();
}
void TearDown() override {
// Cleanup after each test (if needed)
}
};
TEST_F(CalculatorFixtureTest, MemoryStoreAndRecall) {
calc.memoryStore(42.0);
EXPECT_DOUBLE_EQ(calc.memoryRecall(), 42.0);
}
TEST_F(CalculatorFixtureTest, MemoryClear) {
calc.memoryStore(100.0);
calc.memoryClear();
EXPECT_DOUBLE_EQ(calc.memoryRecall(), 0.0);
}
TEST_F(CalculatorFixtureTest, MemoryStartsAtZero) {
EXPECT_DOUBLE_EQ(calc.memoryRecall(), 0.0);
}
*/
// ============================================================================
// EXAMPLE 3: Exception Testing
// ============================================================================
/*
TEST(CalculatorExceptionTest, DivisionByZeroThrows) {
Calculator calc;
EXPECT_THROW(calc.divide(10.0, 0.0), std::invalid_argument);
}
TEST(CalculatorExceptionTest, SqrtOfNegativeThrows) {
Calculator calc;
EXPECT_THROW(calc.sqrt(-1.0), std::domain_error);
}
TEST(CalculatorExceptionTest, ValidDivisionNoThrow) {
Calculator calc;
EXPECT_NO_THROW(calc.divide(10.0, 2.0));
}
TEST(CalculatorExceptionTest, ExceptionMessage) {
Calculator calc;
try {
calc.divide(1.0, 0.0);
FAIL() << "Expected std::invalid_argument";
} catch (const std::invalid_argument& e) {
EXPECT_STREQ(e.what(), "Division by zero");
}
}
*/
// ============================================================================
// EXAMPLE 4: Floating Point Comparisons
// ============================================================================
/*
TEST(FloatingPointTest, UsesDoubleEq) {
Calculator calc;
double result = calc.divide(10.0, 3.0);
EXPECT_DOUBLE_EQ(result, 10.0 / 3.0);
}
TEST(FloatingPointTest, UsesNear) {
Calculator calc;
double result = calc.sqrt(2.0);
EXPECT_NEAR(result, 1.414, 0.001); // Within 0.001 tolerance
}
TEST(FloatingPointTest, ComparesWithTolerance) {
double a = 0.1 + 0.2;
double b = 0.3;
// This might fail due to floating point errors:
// EXPECT_EQ(a, b);
// Use EXPECT_NEAR instead:
EXPECT_NEAR(a, b, 1e-10);
}
*/
// ============================================================================
// CODE UNDER TEST - StringUtils
// ============================================================================
class StringUtils {
public:
static std::string toUpper(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
return result;
}
static std::string toLower(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
}
static std::string trim(const std::string& str) {
size_t start = str.find_first_not_of(" \t\n\r");
if (start == std::string::npos) return "";
size_t end = str.find_last_not_of(" \t\n\r");
return str.substr(start, end - start + 1);
}
static bool startsWith(const std::string& str, const std::string& prefix) {
return str.rfind(prefix, 0) == 0;
}
static bool endsWith(const std::string& str, const std::string& suffix) {
if (suffix.size() > str.size()) return false;
return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
static std::vector<std::string> split(const std::string& str, char delimiter) {
std::vector<std::string> result;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != std::string::npos) {
result.push_back(str.substr(start, end - start));
start = end + 1;
end = str.find(delimiter, start);
}
result.push_back(str.substr(start));
return result;
}
};
// ============================================================================
// EXAMPLE 5: String Testing
// ============================================================================
/*
TEST(StringUtilsTest, ToUpperConvertsLowercase) {
EXPECT_EQ(StringUtils::toUpper("hello"), "HELLO");
}
TEST(StringUtilsTest, ToUpperPreservesUppercase) {
EXPECT_EQ(StringUtils::toUpper("HELLO"), "HELLO");
}
TEST(StringUtilsTest, ToUpperHandlesMixedCase) {
EXPECT_EQ(StringUtils::toUpper("HeLLo WoRLD"), "HELLO WORLD");
}
TEST(StringUtilsTest, ToUpperHandlesEmpty) {
EXPECT_EQ(StringUtils::toUpper(""), "");
}
TEST(StringUtilsTest, TrimRemovesLeadingSpaces) {
EXPECT_EQ(StringUtils::trim(" hello"), "hello");
}
TEST(StringUtilsTest, TrimRemovesTrailingSpaces) {
EXPECT_EQ(StringUtils::trim("hello "), "hello");
}
TEST(StringUtilsTest, TrimRemovesBothSides) {
EXPECT_EQ(StringUtils::trim(" hello "), "hello");
}
TEST(StringUtilsTest, TrimHandlesAllWhitespace) {
EXPECT_EQ(StringUtils::trim(" "), "");
}
TEST(StringUtilsTest, StartsWithTrue) {
EXPECT_TRUE(StringUtils::startsWith("hello world", "hello"));
}
TEST(StringUtilsTest, StartsWithFalse) {
EXPECT_FALSE(StringUtils::startsWith("hello world", "world"));
}
TEST(StringUtilsTest, SplitBasic) {
auto parts = StringUtils::split("a,b,c", ',');
ASSERT_EQ(parts.size(), 3);
EXPECT_EQ(parts[0], "a");
EXPECT_EQ(parts[1], "b");
EXPECT_EQ(parts[2], "c");
}
*/
// ============================================================================
// EXAMPLE 6: Parameterized Tests
// ============================================================================
/*
class AdditionTest : public ::testing::TestWithParam<std::tuple<double, double, double>> {
protected:
Calculator calc;
};
TEST_P(AdditionTest, AddsCorrectly) {
auto [a, b, expected] = GetParam();
EXPECT_DOUBLE_EQ(calc.add(a, b), expected);
}
INSTANTIATE_TEST_SUITE_P(
AdditionTests,
AdditionTest,
::testing::Values(
std::make_tuple(0.0, 0.0, 0.0),
std::make_tuple(1.0, 1.0, 2.0),
std::make_tuple(-1.0, 1.0, 0.0),
std::make_tuple(100.0, 200.0, 300.0),
std::make_tuple(-5.0, -5.0, -10.0),
std::make_tuple(3.14, 2.86, 6.0)
)
);
*/
// ============================================================================
// EXAMPLE 7: Test with Containers
// ============================================================================
/*
TEST(VectorTest, EmptyVectorHasZeroSize) {
std::vector<int> v;
EXPECT_TRUE(v.empty());
EXPECT_EQ(v.size(), 0);
}
TEST(VectorTest, PushBackIncreasesSize) {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
EXPECT_FALSE(v.empty());
EXPECT_EQ(v.size(), 2);
}
TEST(VectorTest, ElementAccess) {
std::vector<int> v = {10, 20, 30};
EXPECT_EQ(v[0], 10);
EXPECT_EQ(v[1], 20);
EXPECT_EQ(v[2], 30);
EXPECT_EQ(v.front(), 10);
EXPECT_EQ(v.back(), 30);
}
TEST(VectorTest, ThrowsOnInvalidAt) {
std::vector<int> v = {1, 2, 3};
EXPECT_THROW(v.at(10), std::out_of_range);
}
*/
// ============================================================================
// CODE UNDER TEST - BankAccount
// ============================================================================
class BankAccount {
public:
explicit BankAccount(double initialBalance = 0.0)
: balance_(initialBalance) {
if (initialBalance < 0) {
throw std::invalid_argument("Initial balance cannot be negative");
}
}
double getBalance() const { return balance_; }
void deposit(double amount) {
if (amount <= 0) {
throw std::invalid_argument("Deposit amount must be positive");
}
balance_ += amount;
}
void withdraw(double amount) {
if (amount <= 0) {
throw std::invalid_argument("Withdrawal amount must be positive");
}
if (amount > balance_) {
throw std::runtime_error("Insufficient funds");
}
balance_ -= amount;
}
bool transfer(BankAccount& to, double amount) {
try {
withdraw(amount);
to.deposit(amount);
return true;
} catch (const std::exception&) {
return false;
}
}
private:
double balance_;
};
// ============================================================================
// EXAMPLE 8: BankAccount Tests
// ============================================================================
/*
class BankAccountTest : public ::testing::Test {
protected:
BankAccount account{100.0}; // Start with $100
};
TEST_F(BankAccountTest, InitialBalanceIsCorrect) {
EXPECT_DOUBLE_EQ(account.getBalance(), 100.0);
}
TEST_F(BankAccountTest, DepositIncreasesBalance) {
account.deposit(50.0);
EXPECT_DOUBLE_EQ(account.getBalance(), 150.0);
}
TEST_F(BankAccountTest, WithdrawDecreasesBalance) {
account.withdraw(30.0);
EXPECT_DOUBLE_EQ(account.getBalance(), 70.0);
}
TEST_F(BankAccountTest, CannotWithdrawMoreThanBalance) {
EXPECT_THROW(account.withdraw(200.0), std::runtime_error);
}
TEST_F(BankAccountTest, CannotDepositNegative) {
EXPECT_THROW(account.deposit(-10.0), std::invalid_argument);
}
TEST_F(BankAccountTest, CannotWithdrawNegative) {
EXPECT_THROW(account.withdraw(-10.0), std::invalid_argument);
}
TEST_F(BankAccountTest, TransferBetweenAccounts) {
BankAccount other{50.0};
EXPECT_TRUE(account.transfer(other, 30.0));
EXPECT_DOUBLE_EQ(account.getBalance(), 70.0);
EXPECT_DOUBLE_EQ(other.getBalance(), 80.0);
}
TEST_F(BankAccountTest, TransferFailsWithInsufficientFunds) {
BankAccount other{0.0};
EXPECT_FALSE(account.transfer(other, 200.0));
// Balances should be unchanged
EXPECT_DOUBLE_EQ(account.getBalance(), 100.0);
EXPECT_DOUBLE_EQ(other.getBalance(), 0.0);
}
*/
// ============================================================================
// EXAMPLE 9: Death Tests
// ============================================================================
/*
void mustNotBeNull(int* ptr) {
if (ptr == nullptr) {
std::abort(); // or std::exit(1)
}
*ptr = 42;
}
TEST(DeathTest, AbortOnNull) {
EXPECT_DEATH(mustNotBeNull(nullptr), "");
}
// Or with assertions:
void assertPositive(int x) {
assert(x > 0 && "x must be positive");
}
TEST(DeathTest, AssertFails) {
// Only works if NDEBUG is not defined
// EXPECT_DEBUG_DEATH(assertPositive(-1), "x must be positive");
}
*/
// ============================================================================
// EXAMPLE 10: Test Utilities
// ============================================================================
/*
// Custom matcher example
MATCHER_P(IsBetween, range, "") {
return arg >= std::get<0>(range) && arg <= std::get<1>(range);
}
TEST(MatcherTest, CustomMatcher) {
int value = 50;
EXPECT_THAT(value, IsBetween(std::make_tuple(0, 100)));
}
// Test with SCOPED_TRACE for better error messages
TEST(ScopedTraceTest, ShowsContextOnFailure) {
Calculator calc;
for (int i = 0; i < 5; ++i) {
SCOPED_TRACE(::testing::Message() << "Iteration " << i);
EXPECT_GE(calc.add(i, 1), i);
}
}
*/
// ============================================================================
// CMakeLists.txt for this project
// ============================================================================
/*
cmake_minimum_required(VERSION 3.16)
project(UnitTestExamples)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)
enable_testing()
add_executable(unit_tests examples.cpp)
target_link_libraries(unit_tests GTest::gtest_main)
include(GoogleTest)
gtest_discover_tests(unit_tests)
*/
// ============================================================================
// MAIN - Demo without Google Test
// ============================================================================
int main() {
cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
cout << "║ GOOGLE TEST EXAMPLES ║" << endl;
cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
cout << "\nThis file contains Google Test examples." << endl;
cout << "To run the tests, uncomment the test code and compile with gtest." << endl;
cout << "\n=== Demo: Calculator ===" << endl;
Calculator calc;
cout << " 5 + 3 = " << calc.add(5, 3) << endl;
cout << " 10 / 2 = " << calc.divide(10, 2) << endl;
cout << " sqrt(16) = " << calc.sqrt(16) << endl;
cout << "\n=== Demo: StringUtils ===" << endl;
cout << " toUpper('hello') = " << StringUtils::toUpper("hello") << endl;
cout << " trim(' hello ') = '" << StringUtils::trim(" hello ") << "'" << endl;
cout << " startsWith('hello', 'he') = " << StringUtils::startsWith("hello", "he") << endl;
cout << "\n=== Demo: BankAccount ===" << endl;
BankAccount account(100.0);
account.deposit(50.0);
cout << " After deposit $50: $" << account.getBalance() << endl;
account.withdraw(30.0);
cout << " After withdraw $30: $" << account.getBalance() << endl;
cout << "\n═══════════════════════════════════════════════════════════════" << endl;
cout << "To compile with Google Test:" << endl;
cout << " mkdir build && cd build" << endl;
cout << " cmake .." << endl;
cout << " cmake --build ." << endl;
cout << " ctest --output-on-failure" << endl;
return 0;
}