Skip to content

SqlProof Class

The SqlProof class is the entry point for all property tests. Create one instance per test suite via SqlProof.connect(), share it across all check() and invariant() calls, then call disconnect() in cleanup.

SqlProof.connect(options)

Factory method. Connects to Postgres (or starts a testcontainers instance), introspects the schema, and returns a ready SqlProof instance.

static async connect(options: SqlProofConnectOptions): Promise<SqlProof>

Options:

FieldTypeDescription
schemaFilestringPath to a .sql DDL file. Auto-starts a testcontainers Postgres.
connectionStringstringpostgresql:// URL. Connects to an existing Postgres instance.
schemastringPostgres schema name to introspect. Default: 'public'. Only used with connectionString.

Exactly one of schemaFile or connectionString must be provided.

Example:

// With a SQL file (auto-manages Postgres via testcontainers):
const proof = await SqlProof.connect({ schemaFile: './schema.sql' });
// With an existing database:
const proof = await SqlProof.connect({
connectionString: 'postgresql://localhost:5432/mydb',
});

proof.customize(table, overrides)

Register custom generators or FK distribution strategies for a table. Returns this for fluent chaining. Must be called before check() or invariant().

customize(table: string, overrides: TableCustomization): this

Example:

import fc from 'fast-check';
proof
.customize('products', {
price: fc.float({ min: 0.01, max: 9999.99, noNaN: true }),
name: fc.string({ minLength: 1, maxLength: 100 }),
})
.customize('orders', {
fkDistribution: { customer_id: 'zipf' },
});

proof.check(name, options)

Run a property-based test. Throws SqlProofError on failure with a formatted counterexample including a reproducible seed.

async check(name: string, options: CheckOptions): Promise<void>

Example:

await proof.check('order totals are non-negative', {
generate: { customers: 10, orders: 50, line_items: 200 },
property: async (db) => {
const result = await db.query('SELECT total FROM orders');
return result.rows.every(row => Number(row.total) >= 0);
},
runs: 100,
});

proof.invariant(name, options)

Declarative shorthand: asserts that a SQL query returns zero rows for all generated datasets.

async invariant(name: string, options: InvariantOptions): Promise<void>
FieldTypeDescription
generateRecord<string, number>Per-table row counts.
querystringSQL query. Must return 0 rows for the invariant to hold.
expectEmptytrueAlways true — makes the intent explicit.
runsnumberNumber of datasets to test. Default: 100.
seednumberReproduce a specific failure.
timeoutnumberPer-run timeout in ms. Default: 5000.

Example:

await proof.invariant('no orphan line items', {
generate: { customers: 10, orders: 50, line_items: 200 },
query: `
SELECT li.id FROM line_items li
LEFT JOIN orders o ON li.order_id = o.id
WHERE o.id IS NULL
`,
expectEmpty: true,
runs: 50,
});

proof.disconnect()

Close the Postgres connection and stop the testcontainers instance (if auto-managed). Call in afterEach or afterAll.

async disconnect(): Promise<void>

Example:

afterEach(async () => {
await proof?.disconnect();
});