mockagen

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: MIT

README

Mockagen

Generate massive amounts of local data blazingly fast.

Compatability

My goal is to make it compatable with Mockaroo's schema format. It does not currently have all the same functionality.

Install

Requires Go 1.27 or later. This fetches, compiles, and installs the binary into $GOBIN (default $GOPATH/bin, usually ~/go/bin):

go install github.com/catdevman/mockagen/cmd/mockagen@latest

To install a specific version:

go install github.com/catdevman/mockagen/cmd/mockagen@v0.1.0

Make sure $GOBIN is on your $PATH so the mockagen command is available:

export PATH="$PATH:$(go env GOPATH)/bin"
Download a pre-built binary

Pre-built binaries for Linux, macOS, and Windows are available on the Releases page.

Build from source
git clone https://github.com/catdevman/mockagen
cd mockagen
make
mv mockagen <somewhere_in_your_path>

Usage

mockagen -config <config_file_path>

You can look at test_data directory for example configuration files.

Config file formats

The -config flag accepts any of the following formats:

Extension Format
.json JSON
.yaml YAML
.toml TOML

Output formats

The file_format field in a config selects how records are written to ./output/<name>.<ext>:

file_format Extension Output
json .json One JSON array of record objects
yaml .yaml A YAML sequence, one item per record
csv .csv RFC 4180 rows, with an optional header
fixed .fixed Fixed-width lines (requires each column's start_position/end_position)
parquet .parquet Snappy-compressed Parquet
fluent .log Log lines in fluentd's out_file shape
sql .sql Multi-row INSERT INTO statements, or a bulk-load bundle
Fixed-width output (fixed)

Every column needs a start_position and an end_position, which are 1-based and inclusive - a column at 1-10 occupies the line's first ten bytes. A config that leaves them out, starts a column at 0, or puts a start past its end is rejected by name before anything is written, rather than panicking inside the encoder or quietly dropping the column.

CSV output (csv)

RFC 4180 rows, one per record. Set include_header: true for a leading header row of column names:

id,full name,email
0dcfa976-4eff-4a24-b5de-f2e15f53a9ae,Ms. Lucy Hartmann,YQVcGFs@gUCfVCO.info

Headers use the same lowercased column names as the json, yaml and parquet keys, so one config describes the same columns whichever format it is written to.

Fields are quoted only where the format requires it - a comma, quote, CR or LF anywhere in the value, or a leading space - which is exactly encoding/csv's rule; the output is byte-identical to what the standard library's writer would produce. One asymmetry worth knowing: a value holding a CRLF reads back with a bare LF, because encoding/csv's reader collapses it inside a quoted field. No encoding avoids that, and the generator does not produce such values today.

This is the fastest of the output formats, and the smallest per record - it is the one to reach for when loading a database in bulk, via COPY FROM (postgres), LOAD DATA INFILE (mysql), BULK INSERT (mssql) or .import (sqlite), all of which are far faster than the INSERT statements the sql format writes.

Log output (fluent)

Each record becomes a single line holding a timestamp, a tag, and the record as a JSON object, separated by tabs - the format fluentd's out_file plugin writes and its out_file parser reads back:

2026-09-20T20:38:09-04:00	mockagen.app.access	{"user":"rqCgpGK","level":"WARN"}

The timestamp is taken when the line is written, so records from the same run commonly share one. Set the tag with a top-level tag field; it defaults to mockagen.<name> when omitted. See test_data/config/logs.schema.yaml for a full example.

SQL output (sql)

Records are written as multi-row INSERT statements, batched 1000 rows per statement:

INSERT INTO "public"."users" ("id", "first_name", "email") VALUES
	('5dcc7536-3c67-483c-be5e-c3dc4e2d12e4', 'Macey', 'ujYtaeA@mLNUxoU.net'),
	('1dd2fece-a28b-4315-ad0e-0edf4bfc2f80', 'Audreanne', 'qlDTJjV@WIgWEmj.org');

Three top-level fields configure it:

Field Default Meaning
dialect postgres Which database's spelling to write
mode insert insert for statements, bulk for a load bundle (below)
table the config name, spaces to underscores Table to insert into, optionally schema-qualified (dbo.users)

dialect accepts postgres, mysql, sqlite and mssql, plus the usual aliases (postgresql, pg, mariadb, sqlite3, sqlserver, tsql). An unrecognised one is an error rather than a fallback. The dialects differ in three places:

  • Identifier quoting - "col" for postgres and sqlite, `col` for mysql, [col] for mssql. Every identifier is quoted, so a column named order needs no special handling.
  • Backslashes - mysql reads backslash as an escape character by default, so each one is doubled; the other three take it literally.
  • Unicode literals - mssql gets an N'...' prefix on any value holding non-ASCII text, without which the literal is varchar and anything outside the server's code page is replaced with ? on insert.

Single quotes are escaped by doubling in all four. NUL bytes are dropped, since no dialect's literal syntax can carry one portably.

The output is INSERT statements only - no CREATE TABLE, no transaction wrapper - so it composes with whatever schema and load script you already have. Nothing is ever written as NULL: the generator has no null support, so a blank column arrives as an empty string and is inserted as one. See test_data/config/users.schema.sql.yaml for a full example.

Bulk loading (mode: bulk)

INSERT statements are the portable way in, not the fast one. With mode: bulk the same config writes a pair of files instead:

output/users-bulk.csv        the data
output/users-bulk.load.sql   the command that loads it

The CSV is exactly what file_format: csv produces; what this adds is the loader, which is the part that differs per database and is easy to get subtly wrong:

dialect command reads the file
postgres \copy client-side, relative to psql's directory
mysql LOAD DATA LOCAL INFILE client-side, needs local_infile=1
sqlite .mode csv + .import client-side (CLI dot-commands, not SQL)
mssql BULK INSERT server-side - see below

The loader names the CSV by bare file name, so the two files are a movable pair: zip them together, unpack them on the database host, and run the script from that directory. Each generated script opens with the exact invocation and the caveats that apply to it.

Two things to know:

  • mssql is the exception. BULK INSERT is executed by the SQL Server process, so the path resolves on the server and relative to its working directory. After unpacking on the server you have to substitute an absolute path, or use the bcp command the script also prints, which does honour a relative name. BULK INSERT also takes no column list and maps fields by position, so the table's column order must match the CSV's - the script prints that order.
  • Empty fields load as empty strings, not NULL. Every dialect can coerce them (NULL '', NULLIF), but only for all columns at once, which would rewrite legitimately empty text. Each script carries the one-line change as a comment rather than applying it.

Two dialect limitations the integration tests pin down:

  • mysql needs --default-character-set=utf8mb4 on the client. mockagen writes UTF-8; without the flag the client announces latin1 and the server stores the mojibake re-encoding of every non-ASCII value. The generated script's invocation includes it.
  • mssql bulk cannot load non-ASCII on SQL Server for Linux. BULK INSERT reads the file in the server's default codepage; CODEPAGE = '65001' is the documented fix and that platform rejects it outright, while neither a UTF-8 collation on the target column nor DATAFILETYPE is a way round it. Use mode: insert there - its N'' literals carry unicode correctly. On Windows SQL Server, add CODEPAGE = '65001' to the generated WITH list.

mode: bulk with dialect: sqlite rejects a schema-qualified table: sqlite's .import takes a bare table argument and would otherwise create a table whose name contains the dot, having exited successfully. See test_data/config/users.schema.bulk.yaml for a full example.

Developer tasks

Tasks are scripts in .config/mise/tasks/. mise tasks lists them:

$ mise tasks
bench             Run the benchmarks once against the working tree
bench-compare     A/B the working tree against another commit, interleaved, then benchstat
build             Build the mockagen binary into ./mockagen
clean             Remove the built binary
lint              Check formatting and run go vet, including the integration build tag
test              Run the unit tests (no containers needed)
test-integration  Run the SQL integration tests (starts and removes its own servers)

Run one with mise run <name>, and pass --help to any that take arguments:

mise run bench-compare --help     # [base] (default main), [rounds] (default 6)
mise run bench-compare HEAD~1 10

mise install provisions the Go toolchain pinned in .config/mise/config.toml to match go.mod. The CI workflows use none of this - they call go directly and take their toolchain from go.mod - so tasks can change freely without touching them.

Integration tests

The unit tests pin what each dialect's bytes look like. The integration tests check those bytes actually load, by running them through real postgres, mysql and sqlite servers (and SQL Server), then reading every value back and comparing it byte for byte:

mise run test-integration

That is the whole workflow. The servers are declared in internal/dbtest, which is the shared harness the integration tests across every package use, and started with testcontainers: each comes up the first time a test needs it - so running one dialect's subtest never starts SQL Server - and all of them are removed when the package's tests finish. testcontainers' reaper removes them even if the run panics or is killed, so a cancelled run cannot leave three database servers behind.

The tests are behind an integration build tag, so mise run test stays fast and touches no containers, and they skip rather than fail where docker is not installed. Each one drives its server's own CLI client rather than a Go SQL driver, because that is what the bulk scripts are written for - psql's \copy and mysql's LOAD DATA LOCAL INFILE are client features a driver never exercises; testcontainers is here for the lifecycle and for Exec/CopyFileToContainer, not for a connection string.

Readiness is checked by running each server's own client inside its container (psql -c "SELECT 1", mysqladmin ping, sqlcmd -Q "SELECT 1") rather than by probing a port, because all three of these images accept a connection before they will answer a query - and postgres in particular reports ready via pg_isready while it is still replaying its init scripts, so the probe has to be a query.

They earn their keep: the quote-everything rule in bulk mode, mysql's ESCAPED BY '', the CR escaping in mysql literals, and both mssql limitations above were all found this way, by loading data that had passed every golden-output test.

Benchmarks

Performance over time is tracked at catdevman.github.io/mockagen/dev/bench.

Every push to main runs the benchmark suite against the previous commit, interleaved on a single runner, and publishes the result. See docs/benchmarking.md for how the comparison works and how to run it locally.

Directories

Path Synopsis
cmd
mockagen command
internal
dbtest
Package dbtest provides the database container test harness and targets used across integration tests in mockagen.
Package dbtest provides the database container test harness and targets used across integration tests in mockagen.
pkg module
mockagen/provider
Package provider registers go-faker tags for Mockaroo field types that go-faker has no built-in generator for.
Package provider registers go-faker tags for Mockaroo field types that go-faker has no built-in generator for.
mockagen/sqlgen
Package sqlgen provides database-specific SQL dialect definitions, identifier quoting, string and value literal formatting, parameter placeholders, and bulk-load script generation for mockagen.
Package sqlgen provides database-specific SQL dialect definitions, identifier quoting, string and value literal formatting, parameter placeholders, and bulk-load script generation for mockagen.
mockagen/types
Package types maintains the catalog of data generator types known to mockagen.
Package types maintains the catalog of data generator types known to mockagen.
schema
Package schema models introspected database table schemas, separates structural facts from data-generation decisions, and translates schemas into mockagen generation specifications.
Package schema models introspected database table schemas, separates structural facts from data-generation decisions, and translates schemas into mockagen generation specifications.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL