How Google Test's TestEventListeners Work: Internal Design and Custom Implementation
Google Test's TestEventListeners system uses a repeater pattern where TestEventRepeater forwards lifecycle callbacks to registered listeners, allowing you to implement custom behavior by subclassing TestEventListener or EmptyTestEventListener and appending your instance to UnitTest::listeners().
The google/googletest framework exposes the complete lifecycle of test execution through the TestEventListeners API. Understanding this internal design enables you to build custom reporters, integrate with CI systems, or capture test metrics without modifying the framework's core. This article examines the three-class architecture behind event propagation and provides a complete implementation guide for custom listeners.
Core Architecture of the TestEventListeners System
The event notification system in Google Test is built around three cooperating classes defined in googletest/include/gtest/gtest.h and implemented in googletest/src/gtest.cc.
The TestEventListener Interface
TestEventListener serves as the pure-virtual interface that defines one method for every test-run event. Located at line 2930 of googletest/include/gtest/gtest.h, this class declares callbacks for program start/end, test iteration, test suite start/end, individual test start/end, test part results, and environment setup/teardown. To receive notifications, you derive from this class and implement the specific callbacks you need.
EmptyTestEventListener Convenience Base
EmptyTestEventListener provides a no-op implementation of every TestEventListener method. Found at line 2925 in googletest/include/gtest/gtest.h, this class allows you to subclass and override only the methods you care about, ignoring the rest. It eliminates boilerplate when you need to monitor just one or two specific events.
TestEventRepeater Internal Hub
TestEventRepeater acts as the internal "hub" that holds a vector of concrete listeners. Defined around line 3848 in googletest/src/gtest.cc, this class forwards every event to registered listeners in insertion order (or reverse order for teardown events). The repeater is owned by TestEventListeners and manages the actual dispatch logic, including a forwarding_enabled_ flag used to suppress events during death-test child processes.
How TestEventListeners Dispatch Events
The framework follows a strict workflow when constructing, registering, and invoking listeners:
1. Singleton Construction
When UnitTest::GetInstance() is first invoked, the singleton creates a TestEventListeners object. The constructor, located at lines 5226-5232 in googletest/include/gtest/gtest.h, instantiates a TestEventRepeater and automatically registers the default console printer (PrettyUnitTestResultPrinter) and XML generator (XmlUnitTestResultPrinter).
2. Listener Registration
User code registers listeners by calling Append() on the facade returned by UnitTest::listeners(). The TestEventListeners::Append() method (lines 5232-5235) forwards the pointer to TestEventRepeater::Append() (lines 3901-3903 in gtest.cc). Ownership transfers immediately to the repeater, which deletes the listener automatically on program exit.
3. Event Propagation
During test execution, the framework invokes methods on the repeater (e.g., OnTestStart()). The repeater checks forwarding_enabled_ and iterates over its internal listeners_ vector, calling the same method on each registered listener. Forward events (startup) dispatch in registration order, while reverse events (teardown) dispatch in reverse order using the GTEST_REVERSE_REPEATER_METHOD_ macro defined near lines 3816-3824.
4. Default Listener Management
The default printers can be replaced or removed via SetDefaultResultPrinter() and SetDefaultXmlGenerator(), located at lines 5272-5287 in gtest.h. These methods handle the special case of the default listeners separately from user-appended ones.
5. Event Suppression
TestEventListeners::SuppressEventForwarding() toggles the repeater's forwarding state. This is automatically disabled in death-test child processes to prevent duplicate output, implemented at lines 5266-5268 in gtest.h and lines 3858-3860 in gtest.cc.
Implementing a Custom TestEventListener
Follow these steps to create and register a custom listener that logs test starts, failures, and results:
Step 1: Derive from testing::EmptyTestEventListener to minimize boilerplate.
Step 2: Override the relevant callbacks. Common hooks include:
OnTestStart(const TestInfo&)– Called immediately before the test body executesOnTestPartResult(const TestPartResult&)– Called for each assertion (EXPECT_/ASSERT_)OnTestEnd(const TestInfo&)– Called after the test completes, including cleanup
Step 3: Register the listener in main() before calling RUN_ALL_TESTS().
// my_listener.h
#include <gtest/gtest.h>
#include <iostream>
class MyListener : public testing::EmptyTestEventListener {
public:
void OnTestStart(const testing::TestInfo& test_info) override {
std::cout << "[MyListener] Starting: "
<< test_info.test_suite_name() << "."
<< test_info.name() << "\n";
}
void OnTestPartResult(const testing::TestPartResult& result) override {
if (result.type() == testing::TestPartResult::kSuccess) return;
std::cerr << "[MyListener] Failure at " << result.file_name()
<< ":" << result.line_number() << " - "
<< result.summary() << "\n";
}
void OnTestEnd(const testing::TestInfo& test_info) override {
std::cout << "[MyListener] Finished: "
<< test_info.test_suite_name() << "."
<< test_info.name()
<< (test_info.result()->Passed() ? " [PASS]" : " [FAIL]")
<< "\n";
}
};
// main.cpp
#include <gtest/gtest.h>
#include "my_listener.h"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
// Append the custom listener before running tests
::testing::TestEventListeners& listeners =
::testing::UnitTest::GetInstance()->listeners();
listeners.Append(new MyListener); // Ownership transferred to Google Test
return RUN_ALL_TESTS();
}
Managing Default Listeners and Suppression
The TestEventListeners facade controls whether the default console and XML output is generated. Call listeners.Release(listeners.default_result_printer()) to remove console output entirely, or use SetDefaultResultPrinter(nullptr) to disable it while keeping the XML generator.
When writing death tests, note that SuppressEventForwarding(true) is automatically invoked in the child process. This prevents your custom listener from receiving events from the death-test subprocess, avoiding stale output or double-logging.
Summary
- TestEventListener is the pure-virtual interface defining all lifecycle callbacks, located in
googletest/include/gtest/gtest.h(line 2930). - EmptyTestEventListener provides no-op implementations so you can override selectively (line 2925).
- TestEventRepeater is the internal dispatcher that forwards events to all registered listeners in
googletest/src/gtest.cc(line 3848). - Register custom listeners via
UnitTest::GetInstance()->listeners().Append()beforeRUN_ALL_TESTS(); ownership transfers to the framework. - The repeater respects
forwarding_enabled_, which is automatically disabled during death tests to prevent duplicate output.
Frequently Asked Questions
When should I register a custom TestEventListener?
Register your listener after calling ::testing::InitGoogleTest() but before RUN_ALL_TESTS() in your main() function. Appending listeners during test execution (for example, inside a test body) is unsupported and may result in missed events or undefined behavior.
Can I remove or replace the default console printer?
Yes. Use listeners.Release(listeners.default_result_printer()) to remove the console output entirely, or call listeners.SetDefaultResultPrinter(new MyPrinter) to substitute your own implementation. The old printer is returned and becomes your responsibility to delete unless you passed nullptr.
Is the TestEventListeners system thread-safe?
The TestEventRepeater does not perform internal locking when dispatching events. In environments using gtest-parallel or custom thread pools, your listener implementation must handle its own synchronization if it accesses shared state. The framework guarantees that a single test's events (start, part results, end) are dispatched sequentially on the same thread.
Who owns the listener memory after calling Append?
Ownership transfers immediately to the TestEventRepeater. The repeater stores the pointer in its internal listeners_ vector and deletes all registered listeners in its destructor. Do not delete the listener yourself after appending it, and ensure listeners are allocated with new, not on the stack.
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 →