How to Implement a Custom Main Function with Catch::Session in Catch2
To implement a custom main function with Catch::Session in Catch2, link your executable against the static Catch2 library (instead of Catch2WithMain), instantiate exactly one Catch::Session object in your entry point, and invoke session.run(argc, argv) to drive the test runner while retaining full programmatic control over initialization, configuration, and teardown.
When integrating Catch2 into complex C++ projects, you often need to perform custom setup before tests run or modify runtime configuration programmatically rather than via command-line arguments. By linking against the base Catch2 static library rather than the Catch2WithMain convenience target, you bypass the framework's default entry point and gain direct access to the Catch::Session facade defined in src/catch2/catch_session.hpp. This class exposes the Clara command-line parser and ConfigData structures, allowing you to override defaults, inject custom logic, or extend the CLI with application-specific options.
Basic Custom Main Implementation
Catch::Session serves as the central entry point that owns the configuration state and drives test execution. According to the Catch2 source code, you must create exactly one Session instance per process. The typical implementation follows a three-step pattern: instantiate the session, optionally perform custom initialization, and delegate to the framework's parsing and execution logic.
#include <catch2/catch_session.hpp>
int main( int argc, char* argv[] ) {
// Custom pre-test initialization (e.g., environment setup, logging)
std::cout << "Initializing test environment...\n";
// Create the single Session instance (required – exactly one per process)
Catch::Session session;
// Run the test suite and capture the exit code
int result = session.run( argc, argv );
// Custom post-test cleanup before exiting
std::cout << "Tests completed.\n";
return result;
}
In src/catch2/catch_session.hpp, the run() method parses the command line via the embedded Clara parser, populates an internal ConfigData object, and executes the test suite, returning an exit code that encodes success or specific failure types.
Controlling Configuration Programmatically
If you need to tweak Catch2's configuration after parsing command-line arguments but before running tests, use applyCommandLine() followed by configData(). This approach validates CLI arguments first—returning a non-zero error code on failure—then exposes a mutable reference to the configuration structure defined in src/catch2/catch_config.hpp.
#include <catch2/catch_session.hpp>
int main( int argc, char* argv[] ) {
Catch::Session session;
// Parse arguments manually; returns non-zero on parse errors
int rc = session.applyCommandLine( argc, argv );
if ( rc != 0 ) return rc;
// Programmatically override configuration defaults
auto& cfg = session.configData();
cfg.reporter = "compact"; // Force compact reporter
cfg.useColour = false; // Disable ANSI colours
cfg.minRequiredTestCases = 5; // Example custom validation
return session.run();
}
For complete control, skip applyCommandLine() entirely and populate the ConfigData structure manually before calling run(). The configData() method returns a reference to the raw configuration data, while config() returns the processed Config object used by framework internals during execution.
Adding Custom Command-Line Options
Catch2 exposes its underlying Clara parser via session.cli(), allowing you to compose additional command-line options on top of the framework's defaults. As implemented in src/catch2/internal/catch_clara.hpp, you can chain new options using the | operator and reinstall the extended parser with session.cli().
#include <catch2/catch_session.hpp>
int main( int argc, char* argv[] ) {
Catch::Session session;
int repetitions = 1; // Variable to bind a new option
using namespace Catch::Clara;
auto cli = session.cli() // Existing Catch2 parser
| Opt( repetitions, "n" )
["-r"]["--repeat"] // Short and long flags
("Repeat test run n times");
session.cli( cli ); // Install the extended parser
int rc = session.applyCommandLine( argc, argv );
if ( rc != 0 ) return rc;
// Use the parsed custom value
for ( int i = 0; i < repetitions; ++i ) {
int result = session.run();
if ( result != 0 ) return result;
}
return 0;
}
This pattern extends the CLI while maintaining full compatibility with Catch2's native arguments like --reporter or --section. The Clara library handles the parsing logic, storing values directly into your bound variables while validating types automatically.
Summary
Implementing a custom main function with Catch2 requires linking against the static Catch2 library and leveraging the Catch::Session API:
- Instantiate exactly one
Catch::Sessionper process to manage configuration and execution state, as required by the headersrc/catch2/catch_session.hpp. - Parse command-line arguments explicitly using
applyCommandLine()to detect errors before testing begins and return appropriate exit codes. - Modify runtime settings via
session.configData()for programmatic overrides of reporters, filters, or colour settings after CLI parsing. - Extend the CLI by composing new Clara options onto
session.cli()for application-specific flags, utilizing the definitions insrc/catch2/internal/catch_clara.hpp. - Reference the official documentation in
docs/own-main.mdfor additional patterns and advanced use cases.
Frequently Asked Questions
Why should I implement a custom main function instead of using Catch2WithMain?
Linking against Catch2WithMain provides a default main() suitable for simple projects, but it prevents programmatic control over configuration and initialization. Implementing a custom main allows you to perform setup or teardown rituals, inject dependencies, and modify ConfigData before tests execute, which is essential for integration testing and complex build environments.
How do I force a specific reporter programmatically without modifying CLI arguments?
After calling session.applyCommandLine(), obtain a mutable reference via session.configData() and set the reporter string member to your desired output format (e.g., "xml", "compact", or "console"). Then invoke session.run() to execute tests with the overridden configuration, bypassing the need for users to specify the reporter via command line.
Can I run the test suite multiple times in a single process?
Yes. After constructing a Catch::Session, you may call session.run() multiple times within a loop. This is useful for stress testing or scenario-based execution where you need to repeat the same test suite with different programmatic configurations. Ensure you reset any necessary global state between runs, as the Session object retains configuration unless explicitly modified via configData().
Where is the Catch::Session class defined?
Catch::Session is defined in src/catch2/catch_session.hpp within the Catch2 repository. This header declares the run(), applyCommandLine(), config(), and configData() methods, along with the cli() accessors for parser manipulation. The companion implementation handles the Clara parser integration and ConfigData lifecycle management according to the architecture documented in docs/own-main.md.
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 →