skip to content

What do Airbyte's connector acceptance tests check before a connector ships?

level: seniorimportance: nice to knowfreq 30%

answer

  1. They drive the built image, not your functions
  2. One shared suite for every connector
  3. Good config passes, broken config must fail
  4. Future-dated state should return nothing
  5. The spec and catalog cannot break silently

basics

~20 s

They run the connector image against a real source and assert protocol conformance: the spec is valid and marks secrets, check passes on good config and fails on bad, discover returns a valid catalog, read returns records matching declared schemas, and incremental reads honour state.

solid answer

~50 s

Acceptance tests are a shared suite configured per connector in `acceptance-test-config.yml` and run against the built image with real credentials. The suites map onto the protocol. **spec**: the connector specification is valid JSON Schema and credentials are marked as secrets. **connection**: `check` succeeds with a working config and fails with a deliberately broken one such as `invalid_config.json`. **discovery**: the catalog is well-formed and, importantly, backward compatible with the previously published version, so a release cannot silently change a stream's schema. **basic_read**: records validate against their declared schemas, and can be compared against a pinned `expected_records.jsonl`. **full refresh**: two consecutive reads return the same records. **incremental**: reading from an `abnormal_state.json` far in the future yields nothing, and a second read from the emitted state returns less than the first. The point is that they test the *contract*, not your internal functions.

code

yaml · 17 lines
yaml
connector_image: airbyte/source-example:dev
acceptance_tests:
  spec:
    tests:
      - spec_path: "source_example/spec.yaml"
  connection:
    tests:
      - config_path: "secrets/config.json"
        status: "succeed"
      - config_path: "integration_tests/invalid_config.json"
        status: "failed"
  incremental:
    tests:
      - config_path: "secrets/config.json"
        configured_catalog_path: "integration_tests/configured_catalog.json"
        future_state:
          future_state_path: "integration_tests/abnormal_state.json"

go deeper

for a junior

Know that Airbyte runs a shared acceptance suite against the connector image before it ships, exercising spec, check, discover and read against a real account.

for a middle

Name the suites and what each asserts, especially that check must fail on an invalid config and that records must validate against the schemas discover declared.

for a senior

Talk about running them in practice: pinned test accounts, relaxing comparisons for volatile fields instead of disabling suites, secret management for test credentials, and reading a backward-compatibility failure as a release decision.

for a principal

Position the suite as the ecosystem's floor for a fleet of connectors — a uniform contract test that makes third-party contributions safe — and be clear about the correctness it cannot cover, so team-owned connectors carry their own state and pagination tests.

## What they are for A connector's unit tests check your parsing code. The acceptance tests check that the artefact the platform will actually run behaves like a connector: the same suite runs against every connector in the ecosystem, driving the built Docker image through the four protocol commands with real credentials against a real (usually sandbox) account. They are configured per connector in `acceptance-test-config.yml`, which points at the image, the secret config, and the fixture files each suite needs, and they are run through the repository's connector tooling rather than invoked directly. ## The suites, mapped to the protocol **Spec.** The connector's specification must be valid JSON Schema, must not use constructs the platform cannot render, and must mark every credential as a secret so it is masked in the UI and redacted from logs. The spec is also checked for backward compatibility against the last published version, because it is a user-facing form: silently renaming a required field breaks every existing configured source. **Connection.** `check` must return success with the real config and failure with a deliberately broken one — typically an `invalid_config.json` fixture with a bad credential. This catches the very common bug of a `check` that always returns success because it calls an unauthenticated endpoint or swallows exceptions. **Discovery.** The catalog must be structurally valid: every stream has a JSON schema, declared sync modes are coherent, and incremental streams declare a cursor. Discovery is also subject to a backward-compatibility check against the previously released catalog, so a change that alters a field's type is surfaced as a breaking change rather than discovered downstream when a destination refuses the data. **Basic read.** The connector reads the configured streams and every record is validated against the schema its stream declared. This is the test that catches the classic mismatch where the schema says a field is an integer and the API returns a string, or where a documented field never actually appears. Optionally the suite compares output against a pinned `expected_records.jsonl` fixture, which asserts exact content rather than mere schema conformance. **Full refresh.** Two sequential full reads must produce the same records. A failure here means the read is nondeterministic — unstable ordering combined with a strict comparison, unfiltered volatile data, or paging that skips rows. **Incremental.** Two checks matter. Given an `abnormal_state.json` holding a cursor value far in the future, the connector should return no records — proving it actually applies the state to the source query rather than ignoring it. And a read starting from the state emitted by a previous read should return strictly less data than a read from scratch, proving state genuinely narrows the extraction. ## Where they get flaky, and how to keep them honest The suite runs against a live account, so anything that changes on the vendor's side moves the result. Pinned expected records rot as sandbox data drifts; timestamps, computed fields and vendor-assigned ids differ every run; ordering is rarely stable across pages. The configuration lets you relax the comparison — not requiring exact ordering, and excluding volatile fields from it — and the discipline is to pin a stable, dedicated test account whose data nobody edits. The alternative failure mode is worse: teams that fight flakiness by disabling suites end up shipping connectors whose incremental behaviour is untested, which is exactly the behaviour most likely to be wrong. Credentials are the other operational cost. The suite needs real secrets, so the connector's test config lives in a secret store rather than the repository, and a connector whose vendor has no free sandbox is hard to test honestly. ## What they do not catch Acceptance tests prove conformance, not correctness of meaning. They will not tell you that your cursor advances past unordered records, that you checkpoint before emitting, that your pagination skips rows under concurrent writes, or that a field you mapped means something different from what the analyst assumes. Those need targeted unit tests and, for the state bugs specifically, a deliberate mid-sync failure test. Treat the acceptance suite as the floor every connector must clear, and your own tests as the part that covers the logic you actually wrote.

  • Why does the incremental suite feed the connector a state value dated far in the future?
    Because a connector that ignores the incoming state still returns records for it. If a cursor beyond all source data yields no rows, the connector is provably applying state to the source query rather than reading everything and hoping the destination sorts it out.
  • Why is the discovered catalog checked for backward compatibility against the previous release?
    Because the catalog is a published contract. Changing a field's type or removing a stream breaks every existing connection and every downstream model built on it. Flagging it at test time turns a silent break into an explicit breaking-change decision with a version bump.
  • Pinned expected records keep failing as the sandbox data changes. What do you do?
    Pin a dedicated test account nobody edits, relax the comparison so ordering is not required and volatile fields such as generated ids and timestamps are excluded, and keep the fixture small. Disabling the suite is the wrong fix — it removes the only check on actual record content.
  • What class of connector bug do acceptance tests reliably miss?
    Semantic and state-ordering bugs: checkpointing before emitting records, advancing a cursor past unordered rows, pagination skipping records under concurrent writes, or a field mapped to the wrong meaning. Conformance is proven; correctness of the logic you wrote still needs your own tests.

saying these in an interview costs you the question

  • Thinking they replace unit tests of parsing logic
  • Assuming they run against mocked HTTP responses
  • Disabling the incremental suite because it is flaky
  • Believing a passing suite proves state handling is correct
  • Not knowing a broken config must make check fail

context