📘 Learning Objectives
After completing this chapter, you will: - Master C++ debugging techniques and tools - Understand unit testing frameworks and practices - Learn about test-driven development (TDD) - Master debugging tools and techniques - Understand code coverage and quality metrics
🎯 Key Concepts
1. Debugging Techniques
- Debugger usage: GDB, LLDB, Visual Studio Debugger
- Breakpoints: Conditional and data breakpoints
- Watch expressions: Variable monitoring
- Call stack analysis: Stack trace analysis
- Memory debugging: Memory leak detection
2. Unit Testing
- Testing frameworks: Google Test, Catch2, Boost.Test
- Test organization: Test suites and fixtures
- Assertions: Test assertions and expectations
- Mock objects: Test doubles and mocking
- Test data: Test data generation and management
3. Test-Driven Development
- TDD cycle: Red-Green-Refactor
- Test-first design: Writing tests before code
- Refactoring: Code improvement with tests
- Test maintenance: Keeping tests up to date
- Test quality: Writing effective tests
4. Debugging Tools
- Static analysis: Clang Static Analyzer, PVS-Studio
- Dynamic analysis: Valgrind, AddressSanitizer
- Memory debugging: MemorySanitizer, ThreadSanitizer
- Performance profiling: Profiling tools
- Code coverage: Coverage analysis tools
5. Quality Assurance
- Code review: Peer review processes
- Static analysis: Automated code analysis
- Continuous integration: Automated testing
- Quality metrics: Code quality measurement
- Documentation: Code documentation practices
🧩 Practice Exercises
Exercise 28.1: Debugging
Use debugger to find and fix bugs.
Exercise 28.2: Unit Testing
Write comprehensive unit tests.
Exercise 28.3: Test-Driven Development
Practice TDD with a small project.
Exercise 28.4: Code Coverage
Achieve high code coverage with tests.
💻 Code Examples
Unit Testing with Google Test
#include <gtest/gtest.h>
#include <vector>
#include <algorithm>
// Function to test
int find_max(const std::vector<int>& data) {
if (data.empty()) {
throw std::invalid_argument("Empty vector");
}
return *std::max_element(data.begin(), data.end());
}
// Test cases
TEST(FindMaxTest, BasicTest) {
std::vector<int> data = {1, 5, 3, 9, 2};
EXPECT_EQ(find_max(data), 9);
}
TEST(FindMaxTest, SingleElement) {
std::vector<int> data = {42};
EXPECT_EQ(find_max(data), 42);
}
TEST(FindMaxTest, EmptyVector) {
std::vector<int> data;
EXPECT_THROW(find_max(data), std::invalid_argument);
}
TEST(FindMaxTest, NegativeNumbers) {
std::vector<int> data = {-1, -5, -3, -9, -2};
EXPECT_EQ(find_max(data), -1);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Mock Objects
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// Interface to mock
class Database {
public:
virtual ~Database() = default;
virtual bool save(const std::string& data) = 0;
virtual std::string load(int id) = 0;
};
// Mock implementation
class MockDatabase : public Database {
public:
MOCK_METHOD(bool, save, (const std::string&), (override));
MOCK_METHOD(std::string, load, (int), (override));
};
// Class under test
class DataManager {
private:
Database* db;
public:
DataManager(Database* database) : db(database) {}
bool store_data(const std::string& data) {
return db->save(data);
}
std::string retrieve_data(int id) {
return db->load(id);
}
};
// Test with mock
TEST(DataManagerTest, StoreData) {
MockDatabase mock_db;
DataManager manager(&mock_db);
EXPECT_CALL(mock_db, save("test data"))
.WillOnce(::testing::Return(true));
bool result = manager.store_data("test data");
EXPECT_TRUE(result);
}
🎓 Key Takeaways
- Use debugger effectively for bug identification
- Write comprehensive tests for code reliability
- Practice test-driven development for better design
- Use debugging tools for systematic bug fixing
- Maintain code quality with continuous testing
🔗 Next Steps
After mastering debugging and testing, proceed to Chapter 29 to learn about advanced topics.
📚 Additional Resources
- C++ Reference: Debugging
- C++ Core Guidelines: Testing
- Practice with testing frameworks and tools