This document collects advice and best practices for writing tests in WebRTC, covering GN macros, GoogleTest/GMock usage, and available utilities.
rtc_cc_test for new unit testsAs part of the “Small Tests” infrastructure, use rtc_cc_test for new unit tests. It defines a library and a standalone binary (prefixed with the folder name and suffixed with _bin). To generate the binary, run autoninja with a “_bin” suffix to the target - autoninja -C out/Default pc:proxy_unittest_bin will generate “out/Default/pc_proxy_unittest_bin” as your binary.
It should normally have a single source file.
Use rtc_test_suite to aggregate rtc_cc_test targets into larger “mega-targets” for CI/CQ (e.g., peerconnection_unittests). Each rtc_cc_test targets must be in exactly one rtc_test_suite target.
Refer to [add-new-test-binary.md](add-new-test-binary.md) for details on how to add the binary for a new test suite to the infrastructure.
rtc_test to define test binariesThe rtc_test template is the traditional way to define test binaries in WebRTC. It automatically adds //test:test_main as a dependency, providing the standard main() function for running GoogleTest.
Example:
import("//webrtc.gni") rtc_test("my_unittest") { sources = [ "my_unittest.cc" ] deps = [ ":my_library", "//test:test_support", ] }
New test suites should be defined using rtc_test_suite and rtc_cc_test.
rtc_library for test supportrtc_library is preferred for most targets (including test support code). It automatically switches between source_set and static_library based on configuration and the testonly flag.
These are documented in the Google testing blog.
Refer to the GMock Matchers Reference for a full list of available matchers.
Always include test/gtest.h and test/gmock.h instead of the raw GoogleTest/GMock headers. These wrappers handle warning suppressions and exports correctly for WebRTC.
EXPECT_TRUE and EXPECT_FALSE only for booleansUse these macros for pure boolean comparisons only. Do not rely on implicit conversions (e.g., checking if a pointer is non-null).
EXPECT_TRUE(pointer); // BAD: implicit conversion to bool EXPECT_TRUE(integer_value); // BAD: implicit conversion
using ::testing::NotNull; EXPECT_NE(pointer, nullptr); EXPECT_THAT(pointer, NotNull()); EXPECT_NE(integer_value, 0);
See the relevant Abseil tips for details.
Note that using at the beginning of the file is recommended in gmock.
EXPECT_THAT for complex evaluationsEXPECT_EQ, EXPECT_LE, EXPECT_LT, EXPECT_GE, EXPECT_GT are acceptable for simple cases. Prefer EXPECT_THAT and matchers for anything with complex evaluations or when better error messages are needed.
EXPECT_TRUE(vec.size() == 1 && vec[0] == "val"); // poor failure message
using ::testing::ElementsAre; EXPECT_THAT(vec, ElementsAre("val")); EXPECT_LE(value, 10); // okay for simple cases
Always put the tested value first and the constant/expected value last in all expectations.
EXPECT_EQ(0, value);
EXPECT_EQ(value, 0);
ASSERT_ for fatal conditionsPrefer EXPECT_ for the normal case. This allows you to report several errors from one single test run.
Use ASSERT_ if failing the condition would cause a crash or undefined behavior in subsequent code, or if failing the check would make later tests irrelevant (e.g., checking if a pointer is null before dereferencing it).
This is also recommended by the GoogleTest primer.
Helper functions in tests that contain expectations (EXPECT_/ASSERT_) are discouraged. Instead, use matchers to make tests more readable and provide better failure messages. You can create custom matchers in three ways:
auto FirstElementIs(int x) { return ::testing::AllOf( ::testing::Not(::testing::IsEmpty()), ::testing::ResultOf([](const auto& v) { return v.front(); }, ::testing::Eq(x))); }
MATCHER_P Macros: For simple parameterized matchers.MATCHER_P(WithId, id, "") { return arg.Id() == id; }
NearMatcher for time types.#include "test/near_matcher.h" // ... EXPECT_THAT(actual_timestamp, webrtc::Near(expected_timestamp, TimeDelta::Millis(5)));
More information about writing matchers is found in the GMock cookbook.
GlobalSimulatedTimeController for simulated timeUse GlobalSimulatedTimeController to run tests that depend on time (e.g., pacing, timeouts) without waiting for real time to pass. It provides a Clock and TaskQueueFactory. Prefer modern simulated time infrastructure over legacy ScopedFakeClock.
Environment using CreateTestEnvironmentThe Environment is the modern way to propagate global utilities (like Clock, TaskQueueFactory, FieldTrials) through the codebase. Tests that exercise any component using an Environment must always create an Environment using webrtc::CreateTestEnvironment (defined in test/create_test_environment.h) and pass it to the components being tested.
Using CreateEnvironment will cause the test to ignore the command line force-fieldtrials flag, so this should not be used in tests unless there are specific reasons for using it.
WaitUntil when neededwebrtc::WaitUntil (located in test/wait_until.h) is a modern utility for waiting for a condition to become true. It is best to avoid polling where possible, but when needed, use WaitUntil instead of manual polling or sleeping. It polls a predicate using the TimeController.
RunLoop for single-threaded async simulationwebrtc::test::RunLoop (located in test/run_loop.h) is a helper class for tests that need to process tasks posted to a task queue but still want to run everything on a single thread. It is useful for simulating async operations simply.
Some of the available frameworks:
Scenario (test/scenario): Best for network and media quality evaluation (e.g., bandwidth estimation, congestion control).PeerScenario (test/peer_scenario): Best for signaling and PeerConnection API level tests that require multiple threads and a simulated network but want to stay lightweight. **PCLF** (api/test/pclf`) is a framework for full integration tests.IntegrationTestHelpers (pc/test/integration_test_helpers) is an older set of tools that is heavily used for PeerConnection-related unit testing.Other frameworks for more specialized purposes also exist. <--! Question: Should we recommend one over the others? -->
A lot of classes have existing mocks. Mocks for classes used in api live in api/test; there are some in test, but in general, mocks for internal classes should live close to the class they mock.
Mocks should be “pure mocks” using gmock. If the test double needs to have code in it, it should be called a fake. See this testing blog for terminology.
General advice on mocking is in the GMock cookbook.
FrameGenerator: Located in test/frame_generator.h; it is useful for generating video frames for testing video pipelines.gtest-parallel for faster local executionFor faster local execution, use third_party/gtest-parallel/gtest-parallel <binary>.
Note that some tests are timing dependent and may be flaky when run under gtest-parallel.