Testing overview
Connector bugs are expensive to find in production: a crawl either indexes nothing, indexes the wrong thing, or — worst case — deletes documents it should have kept. The SDK's testing tools exist so you find those problems before pointing a connector at a real instance.
Testing progresses through three phases. Each phase swaps exactly one dependency from mocked to real, so a failure tells you which layer broke.
run_connector with a static data client. No network at all.
Records real source responses to NDJSON, then replays them offline.
No mocking. Uploads to whatever GLEAN_SERVER_URL points at.
Most of your tests should be Phase 1. Phase 2 catches the class of bug you can't
mock your way to — a source field that's null more often than the docs
suggest. Phase 3 is a pre-release check, not something you run on every commit.
The harness
TestHarness wraps a connector and drives all three phases:
from glean.indexing.testing import TestConfig, TestHarness
harness = TestHarness(
connector=my_connector,
config=TestConfig(),
clients={"data_client": real_data_client}, # required for Phase 2 and 3
)
clients maps connector attribute names to data client instances. Keys must
match real attributes on the connector — AttributeError otherwise. Phase 1
doesn't need it.
Output validation
run_connector() and the harness both run validate_connector_output()
automatically, which catches structural mistakes — a document whose datasource
doesn't match the connector, a missing required field — and raises
ConnectorOutputValidationError. You get those checks without writing them.
Testing permissions
Every phase can assert on ACLs. Because permissions are the highest-risk part of a connector, test the negative case explicitly — see Permissions.