How to Implement a Custom Test Event Listener in GoogleTest: A Complete Guide
To create a custom test event listener in GoogleTest, derive from ::testing::EmptyTestEventListener, override the specific callback methods you need, and register the instance via ::testing::UnitTest::GetInstance()->listeners().Append() before invoking RUN_ALL_TESTS().
Implementing a custom test event listener allows you to intercept and react to every phase of test execution within the GoogleTest framework. The TestEventListener interface, defined in googletest/include/gtest/gtest.h, provides pure-virtual callbacks for events ranging from individual test failures to complete program termination. This article demonstrates the exact implementation pattern used in the GoogleTest source code, including registration mechanics and memory ownership semantics.
Understanding the TestEventListener Architecture
GoogleTest exposes two primary mechanisms for building listeners: the abstract TestEventListener interface and the EmptyTestEventListener convenience base class. Understanding the distinction between these components is essential for writing maintainable hook code.
The TestEventListener Abstract Base Class
The TestEventListener interface declares pure-virtual methods that the framework invokes during test execution. Located in googletest/include/gtest/gtest.h, this class defines callbacks such as OnTestProgramStart, OnTestStart, OnTestPartResult, OnTestEnd, and OnTestProgramEnd. Each method receives a reference to a context object—such as UnitTest, TestSuite, or TestInfo—allowing inspection of test names, failure details, and timing data.
The EmptyTestEventListener Convenience Class
Rather than implementing every pure-virtual method yourself, you should inherit from ::testing::EmptyTestEventListener. Also defined in googletest/include/gtest/gtest.h, this class provides no-op implementations for all callbacks. By deriving from EmptyTestEventListener, you only override the methods relevant to your use case, significantly reducing boilerplate code.
Step-by-Step Implementation
Creating a functional custom test event listener requires three distinct steps: class definition, method implementation, and global registration.
- Derive from
EmptyTestEventListenerand declare your overrides. - Implement the callbacks to capture the data you need.
- Register the listener with the global
TestEventListenerscontainer beforeRUN_ALL_TESTS()executes.
The framework assumes ownership of the listener pointer passed to Append() and automatically deletes it during program shutdown.
Registering Your Listener
Registration occurs through the TestEventListeners object returned by UnitTest::GetInstance()->listeners(). The TestEventListeners class, defined in googletest/include/gtest/gtest.h, manages the lifetime and invocation order of all registered observers.
#include <gtest/gtest.h>
class MyListener : public ::testing::EmptyTestEventListener {
// ... implementation ...
};
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::TestEventListeners& listeners =
::testing::UnitTest::GetInstance()->listeners();
listeners.Append(new MyListener); // GoogleTest takes ownership
return RUN_ALL_TESTS();
}
The listeners() accessor is implemented in googletest/src/gtest-internal-inl.h, providing the entry point to the global listeners collection.
Practical Implementation Examples
The following patterns demonstrate common use cases for custom test event listeners, from simple logging to complex output formatting.
Logging Test Lifecycle Events
This minimal example logs when each test starts and finishes, reporting the pass/fail status upon completion.
#include <gtest/gtest.h>
#include <iostream>
class SimpleLogger : public ::testing::EmptyTestEventListener {
public:
void OnTestStart(const ::testing::TestInfo& info) override {
std::cout << "[START] " << info.test_suite_name() << '.'
<< info.name() << '\n';
}
void OnTestEnd(const ::testing::TestInfo& info) override {
std::cout << "[END] " << info.test_suite_name() << '.'
<< info.name() << " ("
<< (info.result()->Passed() ? "PASS" : "FAIL") << ")\n";
}
};
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new SimpleLogger);
return RUN_ALL_TESTS();
}
Measuring Test Suite Execution Time
To collect performance metrics, override OnTestSuiteStart and OnTestSuiteEnd. This example uses std::chrono to calculate wall-clock duration for each suite.
#include <gtest/gtest.h>
#include <chrono>
#include <map>
#include <string>
#include <iostream>
class TimingListener : public ::testing::EmptyTestEventListener {
using Clock = std::chrono::high_resolution_clock;
std::map<std::string, Clock::time_point> start_times_;
public:
void OnTestSuiteStart(const ::testing::TestSuite& suite) override {
start_times_[suite.name()] = Clock::now();
}
void OnTestSuiteEnd(const ::testing::TestSuite& suite) override {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
Clock::now() - start_times_[suite.name()]);
std::cout << "[TIMING] Suite " << suite.name()
<< " ran in " << elapsed.count() << " ms\n";
}
};
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TimingListener);
return RUN_ALL_TESTS();
}
Generating Custom JSON Reports
For CI/CD integration, you may want to replace the default console output entirely. This example generates a JSON summary and suppresses the standard printer.
#include <gtest/gtest.h>
#include <iostream>
#include <nlohmann/json.hpp>
class JsonReporter : public ::testing::EmptyTestEventListener {
nlohmann::json report_;
public:
void OnTestProgramStart(const ::testing::UnitTest&) override {
report_["tests"] = nlohmann::json::array();
}
void OnTestEnd(const ::testing::TestInfo& info) override {
nlohmann::json entry;
entry["suite"] = info.test_suite_name();
entry["name"] = info.name();
entry["passed"] = info.result()->Passed();
entry["duration_ms"] =
std::chrono::duration_cast<std::chrono::milliseconds>(
info.result()->elapsed_time()).count();
report_["tests"].push_back(entry);
}
void OnTestProgramEnd(const ::testing::UnitTest&) override {
std::cout << report_.dump(2) << std::endl;
}
};
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
// Remove default console printer to prevent duplicate output
listeners.Release(listeners.default_result_printer());
listeners.Append(new JsonReporter);
return RUN_ALL_TESTS();
}
Replacing the Default Result Printer
If your custom test event listener implements its own output logic, you should remove the built-in console printer to avoid mixed formatting. Call listeners.Release(listeners.default_result_printer()) before appending your custom listener. The Release method removes the listener from the container without deleting it, whereas Append transfers ownership to GoogleTest.
Summary
- Inherit from
EmptyTestEventListener(defined ingoogletest/include/gtest/gtest.h) to avoid implementing all virtual methods. - Override specific callbacks such as
OnTestStart,OnTestEnd, orOnTestSuiteEndto hook into execution phases. - Register via
listeners().Append()beforeRUN_ALL_TESTS(); GoogleTest takes ownership of the pointer. - Remove the default printer using
listeners.Release(listeners.default_result_printer())when implementing custom output formats.
Frequently Asked Questions
How do I access the test name inside a listener callback?
The TestInfo object passed to callbacks like OnTestStart contains test_suite_name() and name() methods. According to the GoogleTest source in googletest/include/gtest/gtest.h, these methods return the C-string names of the current suite and test case, respectively.
Can I have multiple custom test event listeners active simultaneously?
Yes. The TestEventListeners container supports multiple registered observers. Simply call listeners.Append() for each new instance. The framework invokes callbacks in the order in which listeners were appended.
Who is responsible for deleting the listener object?
GoogleTest assumes ownership when you pass a pointer to Append(). The framework deletes all registered listeners during the UnitTest destruction phase. You must not delete the object manually after registration.
How do I prevent the default console output from appearing?
Call listeners.Release(listeners.default_result_printer()) before adding your custom listener. This removes the default PrettyUnitTestResultPrinter from the event chain, allowing your custom test event listener to control all 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 →