How to Create Custom TestEventListeners in GoogleTest: A Complete Implementation Guide

You create custom TestEventListeners in GoogleTest by inheriting from EmptyTestEventListener, overriding lifecycle callback methods such as OnTestStart and OnTestEnd, and registering the instance via UnitTest::GetInstance()->listeners()->Append() before invoking RUN_ALL_TESTS().

The GoogleTest framework exposes a flexible event-notification mechanism that lets you observe and react to the test execution lifecycle without modifying the library source. By implementing custom TestEventListeners in the google/googletest repository, you can build custom logging pipelines, performance monitors, or alternative reporting formats that integrate seamlessly with the existing test infrastructure.

The TestEventListener Architecture

The event system is built around an abstract interface and a convenience base class located in the public header googletest/include/gtest/gtest.h.

The TestEventListener Interface

The abstract class TestEventListener (defined around line 930 in gtest.h) declares pure virtual methods that the framework calls at specific execution points. These callbacks include OnTestProgramStart, OnTestIterationStart, OnTestCaseStart, OnTestStart, OnTestEnd, OnTestCaseEnd, OnTestIterationEnd, and OnTestProgramEnd. Each method receives context-specific references—such as const TestInfo& or const UnitTest&—allowing listeners to inspect test metadata, results, and timing.

The EmptyTestEventListener Base Class

Rather than implementing every pure virtual method, inherit from EmptyTestEventListener (located around line 997 in gtest.h). This class provides empty implementations for all callbacks, enabling you to override only the specific lifecycle events you need. This pattern minimizes boilerplate when implementing custom TestEventListeners.

Implementing a Custom Timing Listener

Below is a complete example that records the duration of each test by capturing a timestamp in OnTestStart and calculating the delta in OnTestEnd.

#include <gtest/gtest.h>
#include <chrono>
#include <iostream>

class TimingListener : public ::testing::EmptyTestEventListener {
 public:
  void OnTestStart(const ::testing::TestInfo& test) override {
    start_ = std::chrono::steady_clock::now();
  }

  void OnTestEnd(const ::testing::TestInfo& test) override {
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> secs = end - start_;
    std::cout << "[  TIME  ] " << test.test_case_name() << '.' << test.name()
              << " : " << secs.count() << " seconds\n";
  }

 private:
  std::chrono::steady_clock::time_point start_;
};

Registering Listeners with the UnitTest Singleton

Listeners are managed by the TestEventListeners collection owned by the UnitTest singleton (accessible around line 1026 in gtest.h). You must register your listener after calling InitGoogleTest but before RUN_ALL_TESTS.

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  
  // Append custom listener to the event queue
  ::testing::UnitTest::GetInstance()->listeners()->Append(new TimingListener);
  
  return RUN_ALL_TESTS();
}

Removing and Replacing Default Output

To suppress the default console printer and use only your custom listener, call Release on the default result printer to take ownership, then delete it. Alternatively, use SetDefaultResultPrinter to substitute your own implementation as the primary output generator.

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  
  auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
  
  // Remove the default printer to silence standard output
  delete listeners.Release(listeners.default_result_printer());
  
  // Install a minimal custom printer
  class MinimalPrinter : public ::testing::EmptyTestEventListener {
   public:
    void OnTestProgramEnd(const ::testing::UnitTest& unit_test) override {
      std::cout << "Total tests run: " << unit_test.total_test_count() << "\n";
    }
  };
  
  listeners.SetDefaultResultPrinter(new MinimalPrinter);
  return RUN_ALL_TESTS();
}

Essential Callback Methods

When implementing custom TestEventListeners, override these key methods to intercept specific events:

  • OnTestProgramStart(const UnitTest&) – Called once before any tests run; use for global setup or header logging.
  • OnTestCaseStart(const TestCase&) – Called before the first test in a test case (test suite) begins.
  • OnTestStart(const TestInfo&) – Called immediately before an individual test starts; ideal for timers or resource tracking.
  • OnTestEnd(const TestInfo&) – Called immediately after a test completes; receives the test result (success or failure).
  • OnTestCaseEnd(const TestCase&) – Called after all tests in a test case finish.
  • OnTestProgramEnd(const UnitTest&) – Called once after all tests complete; use for final summaries or cleanup.

Summary

  • Inherit from EmptyTestEventListener (defined in googletest/include/gtest/gtest.h) to avoid implementing unused callback methods when creating custom TestEventListeners.
  • Register listeners via UnitTest::GetInstance()->listeners()->Append() after InitGoogleTest but before RUN_ALL_TESTS().
  • Remove default output by calling Release() on default_result_printer() and deleting the returned pointer to suppress standard console reporting.
  • Replace the default printer using SetDefaultResultPrinter() to redirect all output through your custom implementation.
  • Access test metadata through arguments like const TestInfo& and const UnitTest& to build detailed custom reports.

Frequently Asked Questions

What is the difference between TestEventListener and EmptyTestEventListener?

TestEventListener is an abstract interface declaring all pure virtual callback methods, while EmptyTestEventListener is a concrete base class that provides empty implementations for every method. You should inherit from EmptyTestEventListener when creating custom TestEventListeners so you only override the specific lifecycle events you need.

How do I disable the default console output when using a custom listener?

Obtain the TestEventListeners collection via UnitTest::GetInstance()->listeners(), then call Release(listeners.default_result_printer()) to detach the default printer. Delete the returned pointer to free the memory, which prevents the framework from printing the standard [ OK ] or [FAILED] messages to stdout.

Can I register multiple custom listeners simultaneously?

Yes. The TestEventListeners class maintains an internal repeater that forwards each callback to every registered listener in sequence. You can call Append() multiple times to add different listeners for distinct concerns—such as one for timing and another for custom report generation—and they will execute independently without interfering with each other.

Which callback should I use to capture test duration accurately?

Override OnTestStart(const TestInfo&) to capture the start time and OnTestEnd(const TestInfo&) to calculate and record the duration. These callbacks fire immediately before and after the test body executes, excluding fixture setup and teardown time. If you need to include fixture lifecycle duration, use OnTestCaseStart and OnTestCaseEnd instead.

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 →