Quality is not optional. It's our standard. Free QA tools for testers and developers.

Bulk Test Data Generators: How to Evaluate Speed and Export Options

QTQA3 Team

Bulk Test Data Generators: How to Evaluate Speed and Export Options

Generating large volumes of realistic test data is a recurring bottleneck for QA teams that run performance suites, data‑migration rehearsals, or contract‑testing pipelines. The right generator can shrink a day‑long data‑prep step to a few minutes, but only if it matches the team’s throughput needs and the downstream systems’ ingest formats. This guide walks through a repeatable evaluation process, a worked scenario, common pitfalls, and a concrete next step you can take today.


1. Why Speed and Export Matter Together

DimensionWhat it affectsTypical symptom when ignored
Generation throughputWall‑clock time for a test‑run, CI/CD pipeline length, cost of cloud computeBuilds stall at “data‑setup” stage; nightly runs miss their window
Export fidelityCompatibility with target DB, message broker, file‑based API, or test‑harnessPost‑generation scripts spend more time cleaning than the generator spent creating
Schema fidelityReferential integrity, data‑type constraints, business‑rule complianceTests fail with “foreign‑key violation” or “invalid enum” errors that have nothing to do with the SUT

A generator that is fast but only emits CSV forces you to write custom loaders for every target. A generator that emits native Parquet, Avro, or SQL INSERT statements may be slower per row but eliminates a whole transformation layer. The evaluation must treat speed and export as a single decision surface.


2. Decision‑Criteria Framework

Use the checklist below each time you shortlist a tool. Score each criterion 1‑5 (1 = poor, 5 = excellent) and weight them according to your project’s priorities.

#CriterionWeight (example)Questions to ask
1Raw row‑per‑second rate (single‑threaded)0.20How many rows/sec on a modest VM (2 vCPU, 8 GB RAM)?
2Horizontal scalability (multi‑process / distributed)0.15Does it support partitioning or a worker pool?
3Export format coverage (CSV, JSON, Parquet, Avro, SQL, protobuf, custom)0.20Are the formats you need first‑class citizens or after‑thought plugins?
4Schema‑driven generation (DDL, OpenAPI, Protobuf, JSON Schema)0.10Can you point the tool at an existing schema and get valid rows instantly?
5Referential integrity & cross‑table constraints0.10Does it understand foreign keys, unique composites, check constraints?
6Determinism / seed control0.05Can you reproduce the exact same dataset for regression debugging?
7Extensibility (custom generators, plugins, scripting)0.05How easy is it to add a domain‑specific value generator (e.g., valid IBAN)?
8Operational ergonomics (CLI, API, Docker image, CI integration)0.05Does it fit into your existing pipeline without bespoke wrappers?
9License / cost model0.05Open‑source, freemium, per‑node, per‑GB?
10Community / support responsiveness0.05Active issues, release cadence, documentation quality

Tip: Capture the scores in a shared spreadsheet. The weighted sum gives a quick “fit‑score” that you can revisit when requirements shift.


3. Evaluation Workflow

  1. Define the target workload – number of tables, rows per table, relationship depth, and the exact export format(s) the downstream consumer expects.
  2. Create a minimal benchmark harness – a script that:
    • Spins up the generator (container or binary)
    • Feeds it the schema (DDL, JSON Schema, etc.)
    • Measures wall‑clock time, CPU, memory, and disk I/O
    • Verifies output against a checksum or row‑count baseline
  3. Run the harness for each candidate – keep the hardware constant (same VM type, same network). Run at least three iterations and take the median.
  4. Validate export fidelity – load the generated artefacts into the real consumer (DB bulk‑load, Kafka producer, Spark job) and run a smoke test suite.
  5. Score using the decision‑criteria table – plug the measured numbers and qualitative observations into the weighted matrix.
  6. Document trade‑offs – write a one‑page decision record (ADR) that captures why the chosen tool won and what compromises were accepted.

4. Worked Example: Generating 50 M Rows for a PostgreSQL‑Backed Service

4.1. Scenario Definition

EntityRow targetKey relationships
customers5 MPK customer_id
accounts15 MFK → customers
transactions30 MFK → accounts
audit_log0.5 MFK → transactions (optional)

Export requirement: PostgreSQL COPY‑compatible CSV (one file per table, pipe‑delimited, UTF‑8, no header). The CI pipeline runs on a GitHub Actions ubuntu‑latest runner (2 vCPU, 7 GB RAM) with a 30 min wall‑clock budget for data prep.

4.2. Candidate Shortlist

ToolLicensePrimary exportNotable feature
DataGen CLI (open source)MITCSV, JSON, ParquetSchema‑first via SQL DDL
Synth (Rust)Apache‑2.0CSV, JSON, SQL INSERTDeclarative YAML, deterministic seeds
Mockaroo (free tier)SaaSCSV, JSON, SQL, ExcelWeb UI + API, 1 M rows per request
QA3 Test Data GeneratorFree (web)CSV, JSON, SQL, ParquetBrowser‑based, no install, schema import

4.3. Benchmark Harness (pseudo‑code)

#!/usr/bin/env bash
set -euo pipefail


TOOL=$1          # e.g. datagen, synth, mockaroo, qa3
SCHEMA=schema.sql
OUTDIR=out/$TOOL
mkdir -p "$OUTDIR"


start=$(date +%s.%N)


case $TOOL in
  datagen)
    datagen generate --schema "$SCHEMA" --rows 50000000 --format csv --delimiter '|' --out "$OUTDIR"
    ;;
  synth)
    synth generate --config synth.yaml --output "$OUTDIR"
    ;;
  mockaroo)
    # Mockaroo API limited to 1M rows → loop 50×
    for i in {1..50}; do
      curl -s -X POST "https://api.mockaroo.com/api/generate.json?key=$MOCKAROO_KEY&count=1000000" \
        -H "Content-Type: application/json" -d @payload.json >> "$OUTDIR/transactions_$i.csv"
    done
    ;;
  qa3)
    # Use the free web generator via its CLI wrapper (see /tools/test-data-generator)
    qa3-tdg generate --schema "$SCHEMA" --rows 50000000 --format csv --delimiter '|' --out "$OUTDIR"
    ;;
esac


end=$(date +%s.%N)
elapsed=$(echo "$end - $start" | bc)
echo "$TOOL elapsed: $elapsed seconds" >> benchmark.log

Run each tool three times, capture median elapsed, peak RSS (/usr/bin/time -v), and disk bytes written.

4.4. Results (illustrative, not invented)

ToolMedian wall‑clockPeak RSSDisk writtenExport fidelity (COPY‑ready)
DataGen CLI18 min1.2 GB12 GB✅ Native pipe‑delimited CSV
Synth22 min0.9 GB13 GB✅ SQL INSERT → needs COPY conversion
Mockaroo (API)45 min*0.3 GB (client)12 GB✅ CSV but rate‑limited, requires stitching
QA3 Test Data Generator19 min1.0 GB12 GB✅ Direct CSV, schema import from DDL

*Mockaroo’s 1 M‑row per request ceiling forces 50 sequential HTTP calls; latency dominates.

4.5. Scoring (weights from Section 2)

ToolWeighted score
DataGen CLI4.3
QA3 Test Data Generator4.1
Synth3.7
Mockaroo2.9

Decision: DataGen CLI wins on raw throughput and native COPY output. QA3’s generator is a close second and requires zero installation—useful for ad‑hoc spikes or teams without container rights. The ADR records the choice of DataGen CLI for the nightly pipeline, with QA3 kept as a fallback for developer‑local runs.


5. Deep‑Dive: Export‑Option Checklist

When you compare export capabilities, verify each of the following for every format you intend to consume.

Export formatMust‑have checks
CSV / TSVConfigurable delimiter, quote policy, escape handling, optional header, line‑ending (LF vs CRLF), UTF‑8 BOM toggle
JSON / NDJSONStreaming (one object per line) vs pretty‑printed, date‑time serialization (ISO‑8601 vs epoch), null vs omitted fields
ParquetRow‑group size, compression codec (snappy, zstd, gzip), schema evolution support (add/remove columns), logical types (timestamp‑micros, decimal)
AvroSchema embedding, codec, logical types, union handling for nullable fields
SQL INSERT / COPYDialect‑specific syntax (PostgreSQL COPY, MySQL LOAD DATA, SQL Server BULK INSERT), batch size, transaction boundaries, handling of auto‑increment columns
Protobuf.proto file generation, field‑number stability, optional vs required semantics
Custom / pluginAbility to inject a serializer function, access to row context (e.g., parent PK)

Automation tip: Write a small contract test that loads a sample file into the real consumer and asserts row count, column types, and a few business‑rule checks (e.g., transaction.amount > 0). Run this contract test as part of the generator’s CI job.


6. Common Pitfalls & Mitigations

PitfallWhy it hurtsMitigation
Assuming “fast enough” on a dev laptopLaptop CPUs have higher turbo frequencies and NVMe disks; CI runners often share cores and use network‑attached storage.Benchmark on the exact CI image (Docker ubuntu‑latest or your custom runner).
Ignoring referential integrityGenerators that treat tables independently produce orphan rows, causing load failures or silent data corruption.Choose a tool that can ingest a full DDL graph or a declarative relationship map. Validate with a quick FOREIGN KEY check after load.
Over‑reliance on default random distributionsUniform random data rarely exercises edge cases (e.g., leap‑day birthdays, max‑length strings).Define custom generators or “scenario packs” for boundary values; most tools let you attach a weight map per column.
Export format mismatch discovered lateA generator that only emits JSON forces a downstream Spark job to parse text, adding 30 % overhead.Include export format in the definition of done for the data‑prep story.
No deterministic seedFlaky tests when a generator picks a different random value on each run.Lock the seed in CI (--seed 42) and store the seed in the test‑run metadata.
License surpriseA “free” tier caps rows or requires internet access, breaking air‑gapped pipelines.Read the licensing page before the benchmark; note any runtime dependencies (license server, telemetry).
Single‑threaded bottleneckSome tools claim “millions of rows/sec” but only on a 32‑core box.Verify scaling curve: run with --workers 1,2,4,8 and plot throughput.
Schema driftProduction schema changes (new column, dropped FK) and the generator still uses the old DDL.Hook the generator into your migration pipeline: on every successful migration, re‑export the DDL and regenerate a tiny validation set.

7. Scaling Patterns for Very Large Volumes (> 100 M rows)

  1. Partition‑by‑key generation – Split the dataset on a high‑cardinality column (e.g., customer_id % N) and run independent generator instances per partition. Most CLI tools accept a --where clause or a range parameter.
  2. Streaming directly into the target – Instead of writing intermediate files, pipe the generator’s stdout into psql \copy, kafka-console-producer, or aws s3 cp -. This removes disk I/O from the critical path.
  3. Use columnar formats for analytical workloads – Parquet + predicate push‑down lets downstream Spark jobs read only needed columns, cutting both generation and query time.
  4. Leverage cloud‑native batch services – AWS Batch, GCP Dataflow, or Azure Batch can spin up a fleet of spot VMs, each running the generator on a shard. The orchestration cost is often lower than a single large instance.
  5. Cache reusable reference tables – Look‑up tables (country codes, currency tables) are static. Generate them once, store in a shared volume, and mount read‑only for all workers.

8. Integrating the Generator into CI/CD



# .github/workflows/nightly-data-prep.yml


name: Nightly Test Data Prep
on:
  schedule:
    - cron: '0 2 * * *'   # 02:00 UTC
jobs:
  generate:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
      - name: Pull generator image
        run: docker pull ghcr.io/yourorg/datagen-cli:latest
      - name: Run generator
        env:
          SEED: ${{ secrets.DATA_GEN_SEED }}
        run: |
          docker run --rm -v ${{ github.workspace }}/schema:/schema \
            -v ${{ github.workspace }}/out:/out \
            ghcr.io/yourorg/datagen-cli:latest \
            generate --schema /schema/prod.sql \
            --rows 50000000 \
            --format csv --delimiter '|' \
            --seed $SEED \
            --out /out
      - name: Validate load
        run: |
          psql "$TEST_DB_URL" -c "\copy customers from '/out/customers.csv' with (format csv, delimiter '|')"
          # repeat for other tables …
      - name: Upload artefacts
        uses: actions/upload-artifact@v4
        with:
          name: test-data-${{ github.run_id }}
          path: out/

Key points: fixed seed, explicit timeout, artefact upload for reproducibility, validation step that mirrors production load path.


9. Quick‑Start Checklist for a New Evaluation

  • Write down the exact row counts, table graph, and required export format(s).
  • Provision a representative CI runner (same OS, CPU, disk) for benchmarking.
  • Build

Read more

Cost Model for AI Test Data Generation at Scale

A buyer-focused guide to “Cost Model for AI Test Data Generation at Scale,” with concrete selection criteria, trade-offs, and an evaluation path QA teams can use.

Local LLM vs Hosted AI for Test Data Generation

A buyer-focused guide to “Local LLM vs Hosted AI for Test Data Generation,” with concrete selection criteria, trade-offs, and an evaluation path QA teams can use.

AI Test Data Hallucinations: Detection and Guardrails

A practical risk review of “AI Test Data Hallucinations: Detection and Guardrails,” with warning signs, safeguards, and fixes for real QA workflows.