Testing
Unit tests vs integration vs TDD, mocks vs fakes vs stubs, code coverage metrics, GTest and Catch2 frameworks, and testing private methods.
5 questions
JuniorTheoryVery commonWhy do we write unit tests? Unit vs integration vs TDD.
Why do we write unit tests? Unit vs integration vs TDD.
Unit tests verify one function/class in isolation with mocks; fast and deterministic. Integration tests run real components together. TDD writes the failing test first, then minimal code to pass.
Common mistakes
- ✗Testing implementation details instead of behaviour — tests that assert on private method calls or internal state break every refactoring; test the public contract
- ✗Writing tests after the code with 100% coverage as the goal — coverage measures which lines ran, not which behaviours were verified; goal is meaningful assertions
- ✗Mocking everything in integration tests — tests with too many mocks verify the mock interactions, not the system; let integration tests use real components
Follow-up questions
- →What is property-based testing and how does it complement example-based tests?
- →How do you test code that depends on the current time or random numbers?
MiddleTheoryCommonHow to test C++ code? Which frameworks exist (GTest, Catch2)?
How to test C++ code? Which frameworks exist (GTest, Catch2)?
Popular C++ test frameworks: Google Test (TEST macros, gmock), Catch2 (header-only, BDD), Boost.Test, and doctest (single-header). All integrate with CMake via ctest.
Common mistakes
- ✗Using
ASSERT_*in fixture setup —ASSERT_*callsreturnon failure, which doesn't work inside non-void functions likeSetUp(); useASSERT_*only inTESTbodies or useASSERT_NO_FATAL_FAILURE - ✗Making tests order-dependent — tests sharing global state without cleanup can pass in isolation but fail when run together; reset state in
TearDown - ✗Skipping test for 'temporary code' — untested code becomes permanent; write at least one smoke test even for prototypes
Follow-up questions
- →How does Google Mock's
EXPECT_CALLandWillOnce/WillRepeatedlywork? - →How do you measure test execution time and identify slow tests in gtest?
MiddleTheoryCommonWhat is a mock? When to use mocks vs fakes vs stubs?
What is a mock? When to use mocks vs fakes vs stubs?
Test doubles replace real dependencies. A stub returns canned values; a mock verifies calls (EXPECT_CALL); a fake is a lightweight working implementation; a spy logs calls; a dummy fills a slot.
Common mistakes
- ✗Mocking everything including value objects — mocking
std::stringor simple DTOs adds noise; only mock things with side effects or I/O - ✗Writing tests that only verify mock interactions without asserting the observable output — the test becomes a mirror of the implementation, not a specification
- ✗Using mocks to test private methods indirectly — if you need a mock for a private collaborator, the class is probably doing too much; split it
Follow-up questions
- →How does
ON_CALLdiffer fromEXPECT_CALLin Google Mock? - →What is seam and how do you inject test doubles without modifying production code?
SeniorTheoryOccasionalWhat is code coverage and how is it measured?
What is code coverage and how is it measured?
Code coverage measures what fraction of code ran during tests: line, branch, function, or MC/DC. Tools: gcov/lcov, llvm-cov. 100% coverage does not imply meaningful assertions.
Common mistakes
- ✗Optimising for coverage percentage instead of test quality — writing tests that execute code without asserting anything inflates coverage without catching bugs
- ✗Treating 80% coverage as always sufficient — for safety-critical or security-sensitive code, 100% branch coverage (MC/DC) may be required
- ✗Not excluding generated or third-party code from coverage reports — inflated uncovered lines from protobuf-generated files distort the picture
Follow-up questions
- →What is mutation testing and how does it reveal inadequate test assertions?
- →How do you integrate code coverage reporting into a CI pipeline with a minimum threshold check?
SeniorTheoryOccasionalHow to test private methods?
How to test private methods?
Prefer testing private methods through the public interface; complex private logic usually means extracting a separate class. When unavoidable, use friend class MyTest; or FRIEND_TEST.
Common mistakes
- ✗Using
#define private public— it changes the class layout for TU that include it and can cause ODR violations; avoid outside of throwaway tests - ✗Adding
frienddeclarations to production headers for every test — pollutes the interface; useFRIEND_TESTwhich scopes the friendship to a named test - ✗Not reconsidering the design — the need to test private methods is a design smell; consider whether the private code should be a separate, independently-testable component
Follow-up questions
- →How does
FRIEND_TESTfrom gtest work at the preprocessor level? - →What is the 'test-induced damage' problem and how does it apply to testing private methods?