Skip to content

Data Testing — Pipelines, Ingestion, Upstream/Downstream & Structured vs Unstructured

What this is: the complete data-testing foundation — what a data pipeline is, how ingestion works, every component explained, snapshots and their role in downstream testing, upstream vs downstream applications with examples, data types, structured vs unstructured data, and how to test each. The hands-on code lives in the companion: Python for Data Testing.


1. What Is a Data Pipeline?

A data pipeline is an automated series of steps that moves data from where it is produced to where it is used, transforming it along the way. Think of it as a factory conveyor belt: raw material (source data) enters at one end, is cleaned, shaped, and combined at stations along the belt, and finished goods (analytics-ready data) come out the other end.

flowchart LR
    SRC["📥 Sources<br/>apps, DBs, APIs, files, events"] --> ING["🚚 Ingestion<br/>batch or streaming"]
    ING --> STG["🗃️ Staging / Raw zone<br/>land data as-is"]
    STG --> TR["⚙️ Transformation<br/>clean · join · aggregate"]
    TR --> ST["🏛️ Storage<br/>warehouse / lake"]
    ST --> SRV["📤 Serving<br/>BI, ML, APIs, reports"]
    ORCH["🕹️ Orchestration (Airflow etc.)"] -.-> ING & TR & SRV
    MON["📊 Monitoring & data quality"] -.-> ING & TR & ST

Two dominant patterns:

Pattern Order Where transform happens Typical stack
ETL (Extract → Transform → Load) Transform before loading A processing engine mid-pipeline Informatica, SSIS, Spark
ELT (Extract → Load → Transform) Load raw first, transform inside the warehouse The warehouse itself (SQL) Fivetran + dbt + Snowflake/BigQuery

Modern cloud stacks favour ELT: land everything raw (cheap storage), then transform with versioned SQL — which also makes testing easier, because every intermediate table is queryable.


2. What Is Data Ingestion?

Ingestion is the first pipeline stage: collecting data from source systems and landing it in your platform. The key design (and testing) split is batch vs streaming:

Batch ingestion Streaming ingestion
How Scheduled bulk loads (hourly/nightly) Continuous event-by-event flow
Tech SFTP files, DB extracts, API pulls Kafka, Kinesis, Pub/Sub, CDC
Latency Minutes–hours Seconds or less
Test focus Completeness of each load, schedule reliability, re-run safety Ordering, duplicates, late/out-of-order events, exactly-once semantics
Classic bug Partial file loaded after job crash The same event consumed twice (double-counted revenue)

Ingestion tests that matter most:

  • Row-count reconciliation — source count vs landed count per load (the #1 catch-all).
  • Schema contract — the incoming file/API still has the expected columns and types (upstream teams change things without telling you).
  • Idempotency / re-run safety — re-running yesterday's load must not duplicate rows.
  • Late & malformed data — a corrupt row should go to a reject/quarantine table, not silently vanish or kill the whole load.

3. Components of a Data Pipeline (Each Explained)

Component What it does What QA tests there
Sources Systems that produce data — OLTP databases, apps, SaaS APIs, log files, IoT events Availability, contract/schema, sample-data fidelity
Ingestion layer Moves data in (batch jobs, connectors, Kafka consumers, CDC) Counts, duplicates, ordering, re-run safety
Staging / raw zone Lands data unchanged ("bronze") — the audit copy Raw = source (byte/row fidelity), load timestamps
Transformation engine Cleans, deduplicates, joins, aggregates (dbt models, Spark jobs, SQL) Business-rule correctness, join integrity, null handling
Storage Warehouse (Snowflake, BigQuery, Redshift) or lake (S3/Parquet, Delta) Partitioning, schema evolution, permissions
Serving layer Marts, dashboards, ML feature stores, APIs Query results vs expected, freshness, SLA
Orchestrator Schedules and sequences steps, handles retries (Airflow, Dagster, ADF) Dependency order, failure handling, alert firing
Monitoring / DQ layer Data-quality checks, lineage, freshness alerts (Great Expectations, dbt tests) The tests themselves — do they catch seeded bad data?

4. How a Data Pipeline Works — A Concrete Run

Nightly example: an e-commerce company builds a daily sales report.

01:00  Orchestrator triggers the DAG (the dependency graph of steps)
01:01  Ingest: pull yesterday's orders from the OLTP DB (batch extract)
        → land 84,213 rows into raw.orders  (reconcile: source said 84,213 ✅)
01:10  Ingest: consume clickstream events from Kafka → raw.events
01:20  Transform 1: clean — trim strings, standardise currency, null-check emails
01:30  Transform 2: join orders ↔ customers ↔ products (star schema)
01:45  Transform 3: aggregate → daily_sales_by_region
01:50  DQ gate: row counts within ±5% of 7-day average · no null order_ids ·
        revenue total matches source ledger → PASS
01:55  Publish to the mart; dashboard cache refreshed
02:00  Freshness SLA met (report ready before 08:00 business start)

Every arrow in that run is a test point: did the right number of rows move, did the transformation apply the business rule correctly, and did the quality gate actually gate?


5. Snapshots — and How They're Used in Downstream Testing

A snapshot is a captured copy of a dataset at a specific point in time — "the customers table as of midnight, 1 June."

Why snapshots exist:

  • History for slowly changing data. Source systems overwrite in place (a customer's address today replaces yesterday's). Snapshots (or SCD Type-2 tables built from them) preserve what the data looked like when — essential for "revenue by customer region at the time of purchase".
  • Reproducibility. Live data changes constantly; a snapshot is frozen, so tests against it are deterministic.

How snapshots power downstream testing:

Use How it works
Golden baseline / regression Run the pipeline on a frozen input snapshot → capture the output → that output becomes the expected result. Every code change re-runs on the same snapshot and diffs against the baseline. Any difference = a regression (or an intended change to re-baseline).
Snapshot comparison (before/after) Snapshot a table before and after a migration or refactor; assert row-by-row equality (or explained differences only).
Time-travel debugging "The report was right Monday, wrong Tuesday" → diff the Monday vs Tuesday snapshots of each upstream table to find which input changed.
Test-environment seeding Restore a (masked!) production snapshot into staging so downstream apps are tested against realistic data shapes and volumes.
Drift detection Compare today's snapshot statistics (null %, distinct counts, distributions) against last week's — silent upstream changes surface as drift.

The key testing idea: pin the input, pin the expectation. Non-reproducible inputs are to data testing what non-determinism is to LLM testing — snapshots are how you get determinism back.


6. Upstream vs Downstream Applications

Position is relative to where you stand in the flow: upstream = before you (produces your input), downstream = after you (consumes your output).

flowchart LR
    U1["🏪 POS system"] & U2["🌐 Web/app events"] & U3["🏦 Payment gateway"] & U4["📇 CRM (Salesforce)"] -->|upstream| P["⚙️ YOUR PIPELINE<br/>warehouse & marts"]
    P -->|downstream| D1["📈 BI dashboards<br/>(Power BI / Tableau)"]
    P --> D2["🤖 ML models<br/>(churn, fraud, forecasting)"]
    P --> D3["📧 Marketing automation<br/>(segments, campaigns)"]
    P --> D4["🧾 Finance & regulatory<br/>reports"]
    P --> D5["🔌 Data APIs / partner feeds"]
Upstream examples Downstream examples
What they are Systems that generate the data you ingest Systems that consume what your pipeline produces
Examples OLTP order database, POS tills, website clickstream, payment gateway (Adyen/Stripe), CRM (Salesforce), HR system (Workday), third-party market-data feeds Executive BI dashboards, ML models (fraud scoring, churn prediction), marketing segmentation tools, finance/regulatory reporting, recommendation engines, RAG knowledge bases
Testing concern Contract testing — will their changes break my ingestion? (schema change, new enum value, format shift) Impact testing — will my changes break their consumption? (renamed column kills a dashboard; shifted distribution silently degrades an ML model)
Classic incident Upstream adds a new order status "partially_refunded" → your transform's CASE statement buckets it as NULL → revenue under-reported You "fix" a join that was double-counting → the churn model, trained on the double-counted feature, starts mis-scoring

Rule of thumb: you inherit upstream risk and export downstream risk. Data contract tests guard the first; impact analysis + snapshot regression guard the second.


7. Data Basics — Types of Data

7.1 The three shapes of data

Shape Definition Examples Where stored
Structured Fixed schema — rows and typed columns Orders table, CSV of transactions, customer records Relational DBs, warehouses
Semi-structured Self-describing, flexible nesting — no rigid table JSON, XML, YAML, Avro, API payloads, logs Document DBs, lakes, VARIANT columns
Unstructured No inherent schema at all PDFs, emails, images, audio, video, free text Object storage (S3/blob), content systems

7.2 Common column data types (the vocabulary)

Family Types Testing gotchas
Numeric INT, BIGINT, DECIMAL(p,s), FLOAT Float rounding in money (always use DECIMAL for currency); overflow on BIGINT ids
Text VARCHAR(n), TEXT, CHAR Truncation at n; encoding (UTF-8 vs Latin-1); trailing whitespace
Temporal DATE, TIMESTAMP, TIMESTAMPTZ Timezones (the #1 data bug family); DST edges; epoch vs ISO formats
Boolean BOOLEAN Sources encode as 1/0, "Y"/"N", "true"/"True" — mapping errors
Complex ARRAY, STRUCT/OBJECT, JSON Null vs empty array; nested schema drift
Identifiers UUID, surrogate keys Uniqueness, referential integrity

8. Structured vs Unstructured — How to Test Each

8.1 Testing structured data (the six dimensions of data quality)

Structured data has a schema, so tests are precise assertions on rows and columns:

Dimension Question Example test
Completeness Is anything missing? Row-count reconciliation vs source; email IS NOT NULL for active customers
Uniqueness Any duplicates? COUNT(*) = COUNT(DISTINCT order_id)
Validity Do values obey rules? status IN ('placed','shipped','delivered','cancelled'); amount ≥ 0
Consistency Does it agree across tables/systems? Sum of order_lines.amount = orders.total; warehouse revenue = ledger revenue
Accuracy Does it match reality/source? Sample-based field-by-field compare to source system
Timeliness / freshness Is it up to date? MAX(loaded_at) > NOW() - INTERVAL '2 hours'

Plus structural tests: schema checks (columns, types, order), referential integrity (every customer_id in orders exists in customers), and business-rule tests (discount ≤ price). Tooling: SQL assertions, dbt tests, Great Expectations / Pandera, and pandas-based checks — see the Python companion.

8.2 Testing unstructured data

No schema → you can't assert cell values. Instead you test around the content, at three levels:

  1. File/object level (deterministic): the file exists, size > 0, correct format (magic bytes%PDF, \x89PNG), checksum matches after transfer, count of objects matches manifest, no corrupt/unreadable files.
  2. Extraction level: unstructured data is usually converted to structured/semi-structured downstream (OCR text from PDFs, transcripts from audio, labels from images). Test the extraction pipeline: extraction success rate, text-length sanity vs source, spot-check accuracy on a labelled golden sample (e.g., "these 50 PDFs must yield these key fields"), table/layout fidelity.
  3. Metadata & statistical level: every object has required metadata (source, timestamp, classification); population statistics stay in range (avg document length, language mix, image resolution distribution) — drift here signals upstream change.

Bridge to AI testing: unstructured-data testing is exactly what RAG ingestion testing does with documents (chunking fidelity, OCR accuracy), and extraction quality is scored with the same golden-sample technique as LLM evaluation. Data testing and AI testing meet here.

8.3 Side-by-side

Structured Unstructured
Assertion style Exact (counts, sums, constraints) Indirect (integrity, extraction quality, statistics)
Ground truth The source system A labelled golden sample
Automation SQL / dbt / pandas — fully deterministic File checks deterministic; content checks sample-based
Failure signature Wrong number somewhere Silent content loss (parsed 90% of the PDF, dropped the table)

9. Data Structures Involved in Data Testing

The structures you'll actually manipulate when writing data tests:

Structure What it is Role in testing
Table / relation Rows × typed columns The primary test subject (assert on rows/aggregates)
DataFrame In-memory table (pandas/Spark) The workbench — load, compare, profile datasets in code
Schema Column names + types + constraints The contract you assert against (and version)
Primary / foreign keys Uniqueness + relationships Uniqueness and referential-integrity tests
Partition Physical split (usually by date) Test scope + completeness per partition
Snapshot Point-in-time frozen copy Baselines, diffs, reproducibility (§5)
Dictionary / hash map Key→value lookup Row-level compare by key; O(1) reconciliation
Set Unique unordered values Fast "what's in A but not B" (missing/extra keys)
DAG Directed acyclic graph of steps Orchestration order + lineage/impact analysis
Queue / log Ordered event stream (Kafka topic) Streaming tests — ordering, offsets, exactly-once

The set-difference trick alone (source_keys - target_keys = lost rows, target_keys - source_keys = phantom rows) solves half of real-world reconciliation — implemented in the Python companion.


10. Quick Reference

Question One-line answer
Data pipeline? Automated flow: sources → ingest → transform → store → serve, orchestrated + monitored
Ingestion? Getting data in — batch (scheduled loads) or streaming (continuous events)
ETL vs ELT? Transform before load vs load raw then transform in-warehouse (modern default)
Snapshot? Point-in-time frozen copy → golden baselines, diffs, reproducible tests
Upstream? Systems producing your input (OLTP, CRM, payments) — contract-test them
Downstream? Systems consuming your output (BI, ML, finance) — impact-test for them
Structured testing? Six DQ dimensions + schema + referential integrity, in SQL/dbt/pandas
Unstructured testing? File integrity → extraction quality vs golden sample → statistical drift

Where to Go Next