Requirements & goals:- Features must be deterministic, typed, documented, lineage-traceable, and match model input schema/semantics.- CI should block regressions (logic bugs, schema drift, distribution shifts) before deployment.Testing layers & examples:1) Local/unit tests (fast, run on PR)- Pure function tests: pytest for each transform with deterministic inputs -> expected outputs (edge cases, nulls, types).- Property tests: use hypothesis to assert invariants (e.g., monotonicity, value ranges).- Example (PySpark UDF test skeleton):python
def test_user_age_bucket(spark):
df = spark.createDataFrame([(1, 25),(2,None)], ["id","age"])
out = compute_age_bucket(df).collect()
assert any(row.age_bucket == "20-29" for row in out)
2) Schema & contract tests- JSON schema or Avro for feature sets; enforce field names, dtypes, nullability.- Use CI step to fail on incompatible schema changes (auto-approve only when annotated).3) Statistical/data drift checks (CI + scheduled)- Compute fingerprints on training/reference data: feature means, std, quantiles, cardinalities, histograms, KS/JS divergence.- CI: on PR run lightweight checks against latest canonical snapshot; block if divergence > threshold.- Scheduled pipeline: run more sensitive detectors (e.g., population stability index, PSI) and alert.4) Integration tests with model-serving expectations- Contract tests that assert feature order, names, dtypes and serialized form (JSON/protobuf).- End-to-end smoke: generate golden input -> run feature pipeline -> call model-serving container in CI (or local mock) -> compare prediction with golden output within tolerance.- Shadow/canary: deploy feature change to a small fraction of traffic; compare recent predictions and metrics.5) Metadata, lineage & reproducibility tests- Assert that every feature output includes metadata (source job id, code git sha, input snapshot id, timestamp, schema hash).- Produce deterministic feature hash for a sample row; store and compare between runs to detect non-determinism.- Persist reproducibility artefacts (feature DAG, environment spec, container image/tag) and fail CI if missing.CI gating pipeline (stages):1. Pre-commit hooks: lint, small unit tests.2. PR CI: unit tests, schema checks, property tests, lightweight distribution checks, metadata presence.3. Integration CI: run E2E smoke with model mock or staging model image, contract tests.4. Acceptance: run full data drift checks against canonical training snapshot (longer job) — require explicit approve.5. Canary rollout & monitoring: shadow traffic, real-time drift & prediction monitoring (latency, distribution, calibration). Automatic rollback on threshold breaches.Tools & instrumentation:- Testing: pytest, hypothesis, tox; Spark unit frameworks (pytest-spark).- Contracts & expectations: Great Expectations or Deequ for assertions and profiling.- Drift detection: Evidently, NannyML, or in-house PSI/KS pipelines.- CI/CD: GitHub Actions / GitLab / Jenkins with artifact storage; store provenance in a metadata store (OpenLineage/Marquez).- Model serving: integration test against model container using docker-compose or k8s job.Threshold & governance:- Define numeric thresholds per feature (e.g., PSI < 0.2, JS < 0.1), and SLA for missingness.- Require owner approval for schema changes; auto-block otherwise.Operationalization & runbook:- Alerts to SRE/data-platform Slack; automated data snapshot capture on failure; rollback plan (revert feature artifact tag; route traffic to stable) and postmortem template.Why this works:- Multi-layer testing catches logic bugs early (unit), prevents breaking changes (schema/contracts), detects statistical drift (CI + scheduled), ensures model compatibility (integration/contract), and guarantees reproducibility and traceability (metadata/lineage).