๐งช Dual Testing Strategy - Google Test + Catch2¶
Created: 2025-10-17 Status: ACTIVE โ Approach: Professional Dual Framework
๐ฏ Philosophy: Best Tool for Each Job¶
AudioLab uses TWO testing frameworks in a complementary approach: - Google Test (GTest) - Legacy tests, complex fixtures, mature ecosystem - Catch2 - New tests, BDD style, modern C++20
Why Dual? - โ Leverage strengths of both frameworks - โ No need to rewrite 74+ existing GTest tests - โ Use modern Catch2 for new components - โ Cross-validation: critical features tested by BOTH
๐ Current State¶
Google Test (GTest)¶
- Tests: 74+ existing tests
- Coverage: All 16 CORE subsystems
- Status: Installed via vcpkg
- Use Cases: Existing tests, complex fixtures
Catch2¶
- Tests: 0 (new framework)
- Coverage: Future tests
- Status: Installed via vcpkg
- Use Cases: New tests, BDD scenarios
๐ง When to Use Each Framework¶
Use Google Test When:¶
-
Working with existing tests
-
Complex test fixtures needed
-
Parameterized tests
-
Death tests (testing crashes/assertions)
-
Google Mock integration
Use Catch2 When:¶
-
Writing new tests
-
BDD-style scenarios
-
Simple, readable tests
-
Sections for setup variations
-
Generators and property-based testing
๐ File Organization¶
Directory Structure¶
04_CORE/
โโโ 04_00_type_system/
โ โโโ tests/
โ โ โโโ test_simd_operations.cpp # GTest (existing)
โ โ โโโ test_audio_buffer.cpp # GTest (existing)
โ โ โโโ catch2_buffer_scenarios.cpp # Catch2 (new)
โ โโโ CMakeLists.txt
Naming Conventions¶
Google Test files:
- test_*.cpp - Existing GTest tests
- Example: test_containers.cpp
Catch2 files:
- catch2_*.cpp - New Catch2 tests
- Example: catch2_parameter_scenarios.cpp
Benchmark files (Google Benchmark):
- bench_*.cpp - Performance tests
- Example: bench_ringbuffer.cpp
๐ CMake Integration¶
Root CMakeLists.txt¶
# Enable testing
option(BUILD_TESTING "Build tests" ON)
if(BUILD_TESTING)
enable_testing()
# Find both frameworks
find_package(GTest CONFIG REQUIRED)
find_package(Catch2 3 CONFIG REQUIRED)
# Include Catch2 extras
include(Catch)
# Add test subdirectories
add_subdirectory(04_CORE)
endif()
Per-Component CMakeLists.txt¶
# Google Test executable
add_executable(test_ringbuffer_gtest
tests/test_ringbuffer.cpp
)
target_link_libraries(test_ringbuffer_gtest
PRIVATE
audiolab_core
GTest::gtest_main
)
gtest_discover_tests(test_ringbuffer_gtest)
# Catch2 executable
add_executable(test_ringbuffer_catch2
tests/catch2_ringbuffer_scenarios.cpp
)
target_link_libraries(test_ringbuffer_catch2
PRIVATE
audiolab_core
Catch2::Catch2WithMain
)
catch_discover_tests(test_ringbuffer_catch2)
๐ฏ Critical Components: Dual Coverage¶
For mission-critical components, write tests in BOTH frameworks:
Example: RingBuffer¶
GTest - Unit tests:
// test_ringbuffer.cpp
TEST(RingBufferTest, WriteRead) {
RingBuffer<float, 64> rb;
rb.write(42.0f);
EXPECT_EQ(rb.read(), 42.0f);
}
Catch2 - Scenarios:
// catch2_ringbuffer_scenarios.cpp
SCENARIO("Audio thread uses ring buffer", "[realtime]") {
GIVEN("A ring buffer for audio samples") {
RingBuffer<float, 512> rb;
WHEN("Audio thread writes 256 samples") {
for(int i = 0; i < 256; ++i) rb.write(float(i));
THEN("All samples can be read back") {
for(int i = 0; i < 256; ++i) {
REQUIRE(rb.read() == Approx(float(i)));
}
}
}
}
}
Components Requiring Dual Coverage¶
- โ RingBuffer (lock-free, critical)
- โ Parameter smoothing (audio quality)
- โ AudioProcessor (plugin core)
- โ Fast math (DSP accuracy)
- โ Memory allocators (realtime safety)
๐ Test Metrics¶
Current Stats¶
| Framework | Tests | Coverage | Status |
|---|---|---|---|
| GTest | 74 | 100% subsystems | โ Existing |
| Catch2 | 0 | 0% | ๐ Ready |
| TOTAL | 74 | 16/16 subsystems | ๐ In Progress |
Target Stats (Q1 2026)¶
| Framework | Tests | Coverage | Status |
|---|---|---|---|
| GTest | 74 | Legacy tests | โ Maintained |
| Catch2 | 100+ | New features | ๐ฏ Target |
| Dual | 20 | Critical paths | ๐ฏ Target |
| TOTAL | 194+ | 100% critical | ๐ Goal |
๐ Migration Strategy¶
Short Term (2025 Q4)¶
- โ Install both frameworks
- โ Integrate both in CMake
- โ Document dual strategy
- ๐ Keep all 74 GTest tests as-is
- ๐ Write new tests in Catch2
Medium Term (2026 Q1-Q2)¶
- ๐ฏ Add Catch2 scenarios for critical components
- ๐ฏ Achieve dual coverage on top 20 components
- ๐ฏ Create BDD scenarios for user-facing features
Long Term (2026 Q3+)¶
- ๐ค Evaluate: Keep dual or standardize?
- ๐ค Option A: Keep dual (if both provide value)
- ๐ค Option B: Migrate GTest โ Catch2 (if Catch2 proves superior)
- ๐ค Decision based on team preference + metrics
๐ ๏ธ Running Tests¶
Run All Tests¶
Run Only GTest¶
Run Only Catch2¶
Run Specific Component¶
Run with Google Test filters¶
Run with Catch2 tags¶
๐ Best Practices¶
1. Tag Your Tests¶
// Catch2 - Use tags for organization
TEST_CASE("Fast sine", "[math][dsp][realtime]") { }
// GTest - Use test suite names
TEST(FastMathTest, Sine) { }
2. Consistent Naming¶
// Component: RingBuffer
// GTest: test_ringbuffer.cpp โ test_ringbuffer_gtest
// Catch2: catch2_ringbuffer.cpp โ test_ringbuffer_catch2
3. Share Test Data¶
// test_data/ringbuffer_fixtures.hpp
namespace test_fixtures {
constexpr size_t BUFFER_SIZE = 512;
const std::vector<float> SAMPLE_DATA = {/* ... */};
}
// Use in both GTest and Catch2
4. Document Framework Choice¶
๐ Learning Resources¶
Google Test¶
Catch2¶
โ Decision Matrix¶
| Feature | GTest | Catch2 | Winner |
|---|---|---|---|
| Existing tests | โ 74 tests | โ 0 tests | GTest |
| Modern C++20 | โ ๏ธ Good | โ Excellent | Catch2 |
| BDD scenarios | โ No | โ Yes | Catch2 |
| Complex fixtures | โ Excellent | โ ๏ธ Good | GTest |
| Mocking | โ GMock | โ ๏ธ External | GTest |
| Readability | โ ๏ธ Good | โ Excellent | Catch2 |
| Maturity | โ Very mature | โ Mature | Tie |
| Community | โ Huge | โ Large | Tie |
Conclusion: Use BOTH! ๐ฏ
๐ Contacts¶
- Testing Lead: AudioLab Core Team
- GTest Questions: See existing tests in
04_CORE/*/tests/ - Catch2 Questions: See examples in
03_INFRA/03_04_testing_framework/examples/
Version: 1.0.0 Last Updated: 2025-10-17 Status: ACTIVE - Dual Framework Approach โ