Open-Source Wikis

/

Envoy

/

How to contribute

/

Testing

envoyproxy/envoy

Testing

Envoy ships ~15× more lines of test code than core data-plane code (see by the numbers). Almost any non-trivial change is expected to come with both unit and integration coverage. This page is a tour of the test tree.

Test taxonomy

Type Lives under Runs as Purpose
Unit test/common/, test/server/, test/extensions/ bazel test Single class or small graph in isolation, with mocks
Integration test/integration/ bazel test Full Envoy process + fake upstreams + fake xDS, real sockets
Benchmark test/benchmark/, inline *_benchmark_test.cc bazel run Microbenchmarks via google-benchmark
Fuzz test/fuzz/, inline *_fuzz_test.cc bazel test --config=asan-fuzzer libfuzzer-based fuzzing, also run by OSS-Fuzz
Coverage run via run_envoy_bazel_coverage.sh LLVM coverage Per-directory thresholds in test/coverage.yaml
Mocks test/mocks/ (library only) gMock implementations of every public interface

Running tests

# Run a focused unit test
bazel test //test/common/http:filter_manager_test

# Filter inside a single suite
bazel test //test/common/http:filter_manager_test --test_filter=*WatermarkTest*

# Run all tests for a package
bazel test //test/extensions/filters/http/jwt_authn/...

# Run with sanitizers
bazel test --config=asan //test/common/upstream/...
bazel test --config=tsan //test/common/upstream/...
bazel test --config=msan //test/common/upstream/...

# Run with extra logging (printed for failed tests by default)
bazel test --test_output=streamed //test/common/http:filter_manager_test

# Re-run a single failing test target
bazel test --test_arg=--gtest_filter=FilterManagerTest.MyTest //test/common/http:filter_manager_test

Bazel caches everything; passes are reported as (cached). Pass --cache_test_results=no to force re-execution.

Mocks

test/mocks/ shadows envoy/ one-for-one. Every abstract class in envoy/foo/bar.h has a MockBar in test/mocks/foo/bar.h. Examples:

When you add a new public interface, add the matching mock in the same PR. The check_format.py script will not catch a missing mock, but reviewers will.

Integration tests

The integration framework starts a real Envoy in-process, with fake downstream clients (TCP, HTTP/1, HTTP/2, HTTP/3) and fake upstreams. The base classes are in test/integration/:

Specialised flavours:

Tests follow this skeleton:

TEST_P(MyIntegrationTest, MyScenario) {
  initialize();                                  // start Envoy + fake upstreams
  codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http")));
  IntegrationStreamDecoderPtr response =
      codec_client_->makeRequestWithBody(
          Http::TestRequestHeaderMapImpl{{":method", "GET"}, ...}, 1024);
  waitForNextUpstreamRequest();                  // upstream side
  upstream_request_->encodeHeaders(...);
  ASSERT_TRUE(response->waitForEndStream());
  EXPECT_EQ("200", response->headers().getStatusValue());
}

The INSTANTIATE_TEST_SUITE_P macro at the bottom of each file expands the test across IP versions and HTTP versions; this is how a single test can produce dozens of test runs.

The integration README.md has the canonical guide: test/integration/README.md.

Test utilities

test/test_common/ provides fixtures and matchers:

  • Utility::loadFromYaml<T>(...) for parsing protobuf YAML.
  • MockFunction and Test::Global for managing global state across tests.
  • Event::SimulatedTimeSystem for tests that need controlled time.
  • The thread-aware TestRandomGenerator for reproducibility.

Fuzz tests

Fuzzers live in two places:

Common patterns:

  • Code-driven fuzzers use libfuzzer directly with a raw byte input.
  • Protobuf-driven fuzzers use libprotobuf-mutator to fuzz typed inputs (e.g. an envoy::config::route::v3::RouteConfiguration).
  • Capture-replay fuzzers (h1_capture_fuzz_test.cc, h2_capture_fuzz_test.cc) replay captured wire traffic against a real Envoy.

OSS-Fuzz runs all of them continuously and files issues against the Envoy security team.

Coverage gates

test/coverage.yaml lists per-directory minimum coverage. CI fails if a directory drops below its target. The default is high (≥ 96% for most of source/common/ and source/extensions/); some carve-outs exist for hard-to-test areas.

Run a coverage build locally with:

test/run_envoy_bazel_coverage.sh

The HTML report lands in generated/coverage/.

Adding a test

The pragmatic checklist when you change source/common/foo/bar.cc:

  1. Add or extend a unit test in test/common/foo/bar_test.cc. Use mocks for everything outside bar.cc.
  2. If you changed observable behaviour, add or extend an integration test under test/integration/.
  3. If you fixed a parser/codec bug, add a corpus seed under the appropriate *_corpus/ directory and let OSS-Fuzz exercise it.
  4. Run bazel test //test/common/foo/... and bazel test --config=asan //test/integration:filter_integration_test (or the relevant integration target).
  5. If you added a new public interface in envoy/, add the matching mock in test/mocks/.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Testing – Envoy wiki | Factory