Test a change

Choose the lowest-cost test layer that proves the changed behavior, then expand with risk.

Updated 9 August 2026 · 2 min read

On this page

Start at the lowest-cost layer that can prove the contract. Move upward when the behavior depends on Kubernetes API semantics, controller wiring, or a real workload.

ChangeFirst useful layerWhat it proves
Pure Go logic, parsers, renderers, helpersUnit testsDeterministic in-process behavior
Builders, manifests, patches, fake-client contractsUnit and focused package testsEmitted resource shape without API-server semantics
Reconciliation, finalizers, status, admission, defaultingEnvTest integrationReal API-server behavior
Networking, storage, upgrades, backup, restore, workload startupKind E2EController and workload behavior in a cluster
Platform compatibility, disaster recovery, performanceScheduled or focused environment validationEvidence for environment-specific assumptions

The controller-runtime fake client is not a replacement for the API server. Use EnvTest when the test depends on validation, defaulting, subresources, watches, cache wiring, Generation, or ResourceVersion.

verify

Run the baseline before review

devenv test
devenv tasks run operator:bootstrap
devenv tasks run operator:doctor
devenv tasks run operator:ci-core

Run specialized lanes

  • Use make fuzz for parsers, renderers, auth, or normalization code. Set FUZZTIME or FUZZ_TARGET_FILTER for a focused run.
  • Use make verify-perf for changes that may affect reconcile cost, convergence, startup, or lifecycle timing.
  • Use make test-e2e-existing for OpenShift and other platform-specific checks that Kind cannot reproduce.
  • Use make test-e2e-operator-upgrade when controllers, CRDs, Helm, profiles, or migrations must preserve resources made by the prior stable operator.
  • Use the manual HSM lane for KMIP and PKCS#11 paths that require test-only provider fixtures.

E2E suites are declared in test/e2e/suites.yaml. Update the suite owner, risk tier, isolation class, labels, coverage tags, CI lane, nightly policy, and parallelism whenever an E2E spec changes ownership or scope. Then run make verify-e2e-manifest.

Set E2E_FAIL_ON_EMPTY=true with label filters. Write Ginkgo JSON and JUnit reports so a failed lane retains selected specs, failure details, and slow-test evidence.

make test-e2e-ci \
  E2E_LABEL_FILTER='lifecycle && !openshift' \
  E2E_JUNIT_REPORT=artifacts/e2e-reports/local/junit.xml \
  E2E_JSON_REPORT=artifacts/e2e-reports/local/ginkgo.json \
  E2E_FAIL_ON_EMPTY=true

Continue with CI routing.

Search the handbook

Try “install”, “threat model”, or “compatibility”.