How to Debug Failing Tests with Assertion Info and Timing in Catch2
Catch2 provides deep debugging visibility through its reporter infrastructure, exposing full assertion details via AssertionInfo and precise section timing through SectionStats, controllable via command-line flags or custom reporters.
When a test fails in the Catch2 framework, you need more than just a pass/fail signal to diagnose the issue. You need to know exactly which assertion failed, where it lives in your source code, and how long each test section took to execute. This article shows you how to extract rich debugging metadata from Catch2's reporter system to debug failing tests with assertion info and timing.
Understanding the Reporter Infrastructure
The core of Catch2's debugging capability lives in its reporter infrastructure defined in src/catch2/interfaces/catch_interfaces_reporter.hpp. When tests fail, the framework transmits detailed metadata through virtual callbacks like assertionStarting and sectionEnded, giving you programmatic access to exactly what failed and how long each part took.
Capturing Assertion Details with AssertionInfo
Every assertion in Catch2 generates a Catch::AssertionInfo object that records the source location, the original expression, and the macro that generated the assertion. According to the source code in src/catch2/catch_assertion_info.hpp, this struct contains the file path, line number, and macro name【source 3†L17-L20】.
The reporter interface receives this data via IEventListener::assertionStarting( AssertionInfo const& )【source 1†L84-L86】. When you implement a custom reporter or use high verbosity settings, Catch2 forwards this struct for every assertion, allowing you to pinpoint exactly which REQUIRE or CHECK macro failed and where it is located in your codebase.
Tracking Execution Timing with durationInSeconds
Timing information attaches to a SectionInfo object through the field double durationInSeconds, which the framework populates when a section finishes. This value is later exposed through SectionStats and the reporter callback sectionEnded( SectionStats const& )【source 2†L37-L38】.
As implemented in src/catch2/catch_section_info.hpp, this mechanism tracks how long each TEST_CASE or SECTION block takes to execute. When you enable duration reporting, Catch2 highlights sections that exceed your performance thresholds, making it easy to identify slow tests that might indicate performance regressions or flaky timeouts.
Command-Line Debugging Options
You don't need to write custom code to access assertion metadata and timing data. Catch2 exposes these internals through command-line flags that configure the built-in reporters.
Enabling High Verbosity for Assertion Tracing
Use the --verbosity high flag to force the console reporter to invoke assertionStarting for all assertions, not only the failing ones. This gives you a step-by-step trace of every CHECK and REQUIRE as it executes:
./tests --reporter console --verbosity high
This setting targets the shouldReportAllAssertionStarts preference in the reporter configuration, ensuring you see the progression of assertions even in passing tests.
Configuring Duration Reporting
Add the --durations yes flag to display the execution time for each section after the test run. You can also set a specific threshold with --durations <seconds> to highlight only sections slower than the specified value:
./tests --durations 0.5
Sections exceeding 0.5 seconds will be flagged in the output, leveraging the durationInSeconds field populated in SectionStats.
Using XML and JUnit Reporters for CI Integration
For programmatic analysis in CI pipelines, use the XML or JUnit reporters to serialize assertion details and timing data:
./tests --reporter xml > results.xml
The XML output contains <failure> elements with full AssertionInfo (file, line, expression) and <section> elements with a duration attribute, allowing you to parse and trend test performance over time.
Building a Custom Debug Reporter
When the built-in reporters don't suit your needs, you can write a custom reporter by inheriting from IEventListener. This approach lets you capture AssertionInfo and SectionStats in your own data structures.
Implement assertionStarting and assertionEnded to capture assertion metadata, and sectionEnded to log timing. The ReporterPreferences struct lets you opt-in to receive all assertions via shouldReportAllAssertions = true and to get start notifications via shouldReportAllAssertionStarts = true.
Here is a complete example that logs every assertion and section timing to a JSON file:
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <fstream>
class JsonLogger : public Catch::IEventListener {
std::ofstream out;
public:
explicit JsonLogger( Catch::IConfig const* )
: out("catch_debug.json") {
// Opt-in to receive all assertion events
getPreferences().shouldReportAllAssertions = true;
getPreferences().shouldReportAllAssertionStarts = true;
}
void assertionStarting( Catch::AssertionInfo const& info ) override {
out << "{ \"type\":\"assert_start\", \"file\":\""
<< info.file << "\", \"line\":" << info.line
<< ", \"macro\":\"" << info.macroName << "\" }\n";
}
void assertionEnded( Catch::AssertionStats const& stats ) override {
out << "{ \"type\":\"assert_end\", \"passed\":"
<< (stats.assertionResult.isOk() ? "true" : "false")
<< " }\n";
}
void sectionEnded( Catch::SectionStats const& stats ) override {
out << "{ \"type\":\"section\", \"name\":\""
<< stats.sectionInfo.name << "\", \"duration\":"
<< stats.durationInSeconds << " }\n";
}
};
CATCH_REGISTER_REPORTER( "json", JsonLogger )
Compile this reporter into your test binary and invoke it with:
./tests -r json
The generated catch_debug.json will contain every assertion's source location from AssertionInfo and the elapsed time for each section from SectionStats, which you can feed into dashboards or debugging tools.
Summary
AssertionInfocaptures the file path, line number, macro name, and expression for every assertion, defined insrc/catch2/catch_assertion_info.hpp.SectionStatsexposesdurationInSecondspopulated fromSectionInfo, allowing you to identify slow test sections.- Command-line flags
--verbosity highand--durations yesexpose this data through built-in reporters without code changes. - Custom reporters inherit from
IEventListenerand useCATCH_REGISTER_REPORTERto implement bespoke logging of assertion starts/ends and section timing. - XML/JUnit reporters serialize the same metadata for CI pipelines, containing
<failure>elements with full source location and<section>elements with duration attributes.
Frequently Asked Questions
How do I see every assertion including passing ones?
Use the --verbosity high command-line flag. This sets the reporter preference shouldReportAllAssertionStarts = true, causing the framework to call assertionStarting for every assertion, not just failures.
Can I filter to show only slow test sections?
Yes. Use --durations <threshold> where <threshold> is a floating-point number of seconds. Catch2 will only highlight sections whose durationInSeconds exceeds this value, making it easy to spot performance bottlenecks.
Where is assertion source information stored in the Catch2 source?
Assertion source details live in src/catch2/catch_assertion_info.hpp, which defines the AssertionInfo struct containing the file path, line number, and macro name used in the assertion.
How do I export timing data to JSON?
Create a custom reporter inheriting from IEventListener that implements sectionEnded( SectionStats const& ) to capture stats.durationInSeconds. Write this data to a JSON file in your reporter implementation and register it with CATCH_REGISTER_REPORTER. Run your tests with -r <your_reporter_name> to generate the output.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →