Generates **property-based tests** that use randomized input generation to validate invariants and contracts (rather than hand-picked examples). Triggers when the conversation involves: PBT frameworks (Hypothesis library for Python, fast-check for TypeScript, proptest for Rust, rapid for Go, RapidCheck for C++); concepts like invariants, contracts, round-trip symmetry, encode/decode, serialize/deserialize, generative testing, or shrinking; or requests to find edge cases that example-based tests miss — e.g., "find edge cases automatically", "test all possible inputs", "verify this property holds". Does NOT trigger for: writing regular example-based unit tests, debugging, CI/CD setup, UI/component testing, or integration/E2E testing. Identifies up to 7 property patterns (round-trip, idempotence, invariance, metamorphic, inverse, ordering, no-crash), designs input generators, writes property tests, and extracts regression tests from failures.
91
90%
Does it follow best practices?
Impact
94%
1.11xAverage score across 5 eval scenarios
Passed
No known issues
Dependency: npm install --save-dev fast-check
import * as fc from 'fast-check';fc.integer() // Any integer
fc.integer({min: 0, max: 100}) // Constrained
fc.float() // Any float
fc.string() // Any string (including empty)
fc.string({minLength: 1, maxLength: 50}) // Sized strings
fc.boolean() // true or false
fc.constant(null) // Constant value
fc.constantFrom('a', 'b', 'c') // Pick from list
fc.array(fc.integer()) // Arrays of integers
fc.array(fc.integer(), {minLength: 1, maxLength: 10}) // Sized arrays
fc.dictionary(fc.string(), fc.integer()) // Dicts
fc.tuple(fc.string(), fc.integer()) // Tuples
fc.oneof(fc.constant(null), fc.string()) // Union types
fc.record({ // Objects (records)
name: fc.string(),
age: fc.integer({min: 0, max: 150}),
})import * as fc from 'fast-check';
describe('sort', () => {
it('should preserve length after sorting', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const originalLen = arr.length;
const sorted = [...arr].sort((a, b) => a - b);
expect(sorted).toHaveLength(originalLen);
})
);
});
it('should produce ordered result', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const sorted = [...arr].sort((a, b) => a - b);
for (let i = 0; i < sorted.length - 1; i++) {
expect(sorted[i]).toBeLessThanOrEqual(sorted[i + 1]);
}
})
);
});
});import * as fc from 'fast-check';
it('should handle division', () => {
fc.assert(
fc.property(fc.integer(), fc.integer(), (a, b) => {
fc.pre(b !== 0); // Skip when b is 0
expect(a / b * b).toBeCloseTo(a);
})
);
});it('should process async', async () => {
await fc.assert(
fc.asyncProperty(fc.array(fc.integer()), async (arr) => {
const result = await processAsync(arr);
expect(result.length).toBe(arr.length);
})
);
});// Map: transform generated values
const evenIntegers = fc.integer().map((x) => x * 2);
// Filter: only keep values matching predicate
const nonZero = fc.integer().filter((x) => x !== 0);
// Chained
const smallPositiveEvens = fc
.integer({min: 1})
.map((x) => x * 2)
.filter((x) => x < 1000);interface User {
name: string;
age: number;
email: string;
}
const userArbitrary: fc.Arbitrary<User> = fc.record({
name: fc.string({minLength: 1, maxLength: 50}),
age: fc.integer({min: 0, max: 150}),
email: fc.string(),
});
it('all created users have a name', () => {
fc.assert(
fc.property(userArbitrary, (user) => {
expect(user.name.length).toBeGreaterThan(0);
})
);
});it('throws on invalid input', () => {
fc.assert(
fc.property(fc.integer(), (x) => {
expect(() => parseAge(x)).toThrow();
})
);
});fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
// test
}),
{ numRuns: 1000 } // Default is 100
);
// Verbose shrinking output
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => { ... }),
{ verbose: true }
);npx jest test_sort.test.ts # Standard Jest run
npx jest --verbose # Verbose output