From harness-claude
Generates exhaustive test cases using fast-check property-based testing for complex inputs, mathematical properties like commutativity, edge cases missed by examples, and serialization roundtrips.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Generate exhaustive test cases automatically using fast-check property-based testing
Generates property-based tests using fast-check (TypeScript/JavaScript with Vitest) and Hypothesis (Python) to find edge cases, verify properties like roundtrips and invariants.
Guides property-based testing for serialization, validation, normalization, and pure functions with property catalog, pattern detection, and library references.
Guides property-based testing with fast-check (JS/TS), hypothesis (Python), proptest (Rust) to verify invariants like round-trip, idempotence on pure functions, serializers, and large input spaces.
Share bugs, ideas, or general feedback.
Generate exhaustive test cases automatically using fast-check property-based testing
npm install -D fast-check
import fc from 'fast-check';
import { describe, it, expect } from 'vitest';
describe('sort', () => {
it('produces output with same length as input', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
expect(arr.sort()).toHaveLength(arr.length);
})
);
});
it('produces sorted output', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const sorted = [...arr].sort((a, b) => a - b);
for (let i = 1; i < sorted.length; i++) {
expect(sorted[i]).toBeGreaterThanOrEqual(sorted[i - 1]);
}
})
);
});
});
fc.string(); // random strings
fc.integer({ min: 0, max: 100 }); // bounded integers
fc.float({ noNaN: true }); // floats without NaN
fc.boolean(); // true/false
fc.date(); // Date objects
fc.uuid(); // UUID strings
fc.emailAddress(); // valid email addresses
fc.array(fc.integer()); // arrays of integers
fc.record({ name: fc.string(), age: fc.integer({ min: 0 }) }); // objects
fc.oneof(fc.string(), fc.integer()); // union of types
it('JSON roundtrip preserves data', () => {
fc.assert(
fc.property(fc.record({ name: fc.string(), age: fc.integer() }), (obj) => {
expect(JSON.parse(JSON.stringify(obj))).toEqual(obj);
})
);
});
it('normalizing email is idempotent', () => {
fc.assert(
fc.property(fc.emailAddress(), (email) => {
const once = normalizeEmail(email);
const twice = normalizeEmail(once);
expect(once).toBe(twice);
})
);
});
const userArbitrary = fc.record({
id: fc.uuid(),
name: fc.string({ minLength: 1, maxLength: 100 }),
email: fc.emailAddress(),
age: fc.integer({ min: 0, max: 150 }),
});
it('validates all valid users', () => {
fc.assert(
fc.property(userArbitrary, (user) => {
expect(validateUser(user).ok).toBe(true);
})
);
});
// If sort fails on [3, 1, -5, 7, 2], fast-check shrinks to
// the minimal failing case, e.g., [1, -1]
fc.assert(
fc.property(fc.string(), (s) => {
/* ... */
}),
{
numRuns: 1000, // Number of random inputs (default: 100)
seed: 42, // Deterministic seed for reproducibility
verbose: true, // Log generated values
endOnFailure: true, // Stop at first failure
}
);
Property-based testing inverts the traditional testing model: instead of specifying inputs and expected outputs, you specify properties that must hold for ALL inputs. The framework generates random inputs and checks the property, then shrinks failing cases to minimal counterexamples.
What makes a good property:
decode(encode(x)) === xf(f(x)) === f(x)sorted(x).length === x.lengthisValid(invalidInput) === false using generated invalid inputsShrinking: When fast-check finds a failing input, it tries smaller variants to find the minimal counterexample. This is invaluable for debugging — instead of "fails on a 500-element array," you get "fails on [0, -1]."
Integration with Vitest: fast-check works as a library called inside it() blocks. No special test runner integration needed. The fc.assert() call throws on failure, which Vitest catches.
Trade-offs: