How to Use DYNAMIC_SECTION for Parameterized Test Sections in Catch2

DYNAMIC_SECTION lets you create runtime-generated test sections whose names are built from values computed during test execution, unlike the compile-time constant names required by the standard SECTION macro.

The DYNAMIC_SECTION macro in the catchorg/Catch2 testing framework enables data-driven testing by allowing section names to be constructed at runtime from variables, generators, or stream expressions. While the standard SECTION macro requires a fixed string literal known at compile time, DYNAMIC_SECTION evaluates its argument each time the enclosing test case runs, producing distinct, parameterized test paths that adapt to dynamic values.

What Is DYNAMIC_SECTION?

Unlike the regular SECTION macro, which requires a compile-time constant string, DYNAMIC_SECTION evaluates its argument dynamically using stream insertion syntax (<<). This allows the same test case to produce different section hierarchies on different iterations, making it ideal for loops, generators, or conditional test logic.

Under the hood, the macro expands to INTERNAL_CATCH_DYNAMIC_SECTION, which instantiates a Catch::Section object whose description is built from the streamed expression.

Header Files and Implementation

The public API is defined in src/catch2/catch_test_macros.hpp, where the DYNAMIC_SECTION macro is declared. The underlying machinery that handles the runtime name construction and section tracking lives in src/catch2/internal/catch_section.hpp, implementing the INTERNAL_CATCH_DYNAMIC_SECTION logic and the Catch::Section class.

How DYNAMIC_SECTION Works

When the test runner encounters a DYNAMIC_SECTION, it performs three steps:

  1. Evaluates the expression (e.g., std::to_string(i)) to construct the section name.
  2. Creates a Catch::Section object with that computed name.
  3. Executes the enclosed statements only if the section is active for the current test run.

If the same DYNAMIC_SECTION block is reached again with a different evaluated name, Catch2 treats it as an entirely new section, branching into a fresh execution path while preserving isolation between runs.

Practical Code Examples

Parameterized Tests with GENERATE

Combine DYNAMIC_SECTION with GENERATE to create distinct sections for each generated value:

TEST_CASE("Dynamic sections with generators") {
    auto i = GENERATE(0, 1, 2);
    DYNAMIC_SECTION("iteration " << i) {
        REQUIRE(i >= 0);
    }
}

Each generated value creates an independent section named "iteration 0", "iteration 1", and so on, appearing as separate entries in the test report.

Loop-Based Dynamic Sections

Use standard loops to generate multiple sections programmatically:

TEST_CASE("Loop-driven dynamic sections") {
    for (int i = 0; i < 3; ++i) {
        DYNAMIC_SECTION("loop index = " << i) {
            INFO("Current index: " << i);
            REQUIRE(i < 3);
        }
    }
}

The loop executes three times, yielding three isolated sections that run independently.

Conditional Sections with Runtime Data

Construct section names from runtime data such as environment variables:

TEST_CASE("Conditional dynamic sections") {
    std::string mode = std::getenv("TEST_MODE") ? std::getenv("TEST_MODE") : "default";
    DYNAMIC_SECTION("mode = " << mode) {
        if (mode == "fast") {
            REQUIRE(true);  // fast-mode specific checks
        } else {
            REQUIRE(false); // default-mode specific checks
        }
    }
}

The section name reflects the runtime environment state, enabling separate reporting for each configuration mode.

Summary

  • Runtime Evaluation: DYNAMIC_SECTION evaluates its name expression at runtime using stream syntax (<<), unlike the compile-time constants required by SECTION.
  • Source Locations: The macro is defined in src/catch2/catch_test_macros.hpp with implementation details in src/catch2/internal/catch_section.hpp.
  • Distinct Paths: Changing the evaluated name between iterations creates entirely new section branches, supporting data-driven and parameterized testing patterns.
  • Integration: Works seamlessly with GENERATE, loops, and conditional logic to produce flexible test hierarchies that adapt to runtime data.

Frequently Asked Questions

What is the difference between SECTION and DYNAMIC_SECTION in Catch2?

The SECTION macro requires a string literal or compile-time constant for its name, while DYNAMIC_SECTION accepts a stream expression evaluated at runtime. This allows DYNAMIC_SECTION to incorporate variable values, generator outputs, or computed strings into the section name, enabling parameterized test structures that SECTION cannot support.

Can I use DYNAMIC_SECTION with data generators?

Yes. DYNAMIC_SECTION pairs naturally with GENERATE to create uniquely named sections for each generated dataset. Because the section name is evaluated after the generator produces its value, each iteration appears as a distinct test path in the output, providing clear isolation and reporting for parameterized tests.

How does Catch2 handle section names that change between runs?

Catch2 treats each unique evaluated name as a separate section entity. When a DYNAMIC_SECTION produces a different string on subsequent executions within the same test case, the framework branches into a new execution path, running the enclosed code exactly once for that specific name while maintaining standard section scoping and lifecycle guarantees.

Is there a performance cost to using DYNAMIC_SECTION?

The primary overhead comes from the runtime construction of the section name string and the associated Catch::Section object creation. While slightly more expensive than the static SECTION macro, this cost is negligible for most test suites and is offset by the flexibility of runtime parameterization, especially when testing against dynamic data sources or large parameterized datasets.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →