A Claude Code skill that writes property-based tests with fast-check and Hypothesis, and proves they can fail.
English · Русский
A property-based testing (PBT) skill for Claude Code, Codex, OpenCode, Cursor and other agents that read Agent Skills. It covers fast-check with Vitest in TypeScript and Hypothesis with pytest in Python. The agent writes new property tests, finds a property in code that seems to have none, reviews existing property tests that may assert nothing, and tells a wrong property from an ambiguous spec or a real bug when a test fails.
An example test checks the inputs someone thought of. A property test states what must hold for every input, lets the library generate hundreds of inputs, and shrinks the one that breaks the statement down to the smallest case. The first real user of a product types three tax IDs into a field built for one. This is the kind of test that finds it before they do.
Property tests written in a hurry go wrong in predictable ways, and the skill fixes those decisions.
- The property is too weak, or it simply restates the implementation.
- The generator only produces the inputs the code expects.
- A precondition throws away nearly every input, so the test runs almost nothing.
- Nobody checked that the test can fail at all.
- A green run gets reported as "correct for all inputs".
| Agent | Command |
|---|---|
| Claude Code, as a plugin | /plugin install property-testing --marketplace nyxandro/skills |
| Claude Code, Codex, OpenCode, Cursor and others | npx skills add nyxandro/property-testing-skill |
| Any agent, by hand | Download the latest .skill archive and unpack it into the agent's skills folder |
Pick one. If you install it two ways, Claude Code loads the skill twice.
More install options
- The one-line plugin install needs Claude Code 2.1.275 or later. On an older version, add the
catalog first with
/plugin marketplace add nyxandro/skillsand then run/plugin install property-testing@nyxandro. - The catalog pins no version, so every commit here reaches plugin users as an update. Run
claude plugin marketplace update nyxandroandclaude plugin update property-testing@nyxandro, or turn on auto-update under/plugin→ Marketplaces. - With
npx skills, add-gto install the skill for every project. To install a given version, add its tag, as innyxandro/property-testing-skill@v1.0.0. - You can also clone this repository and link the
property-testing/folder into your agent's skills folder. There are no scripts and no build step.
Ask for what you need. These requests all start the skill.
- Write property tests for this parser.
- Find the edge cases this function misses.
- Tests are green, but prod breaks on odd input.
- Write a model-based test for this cache.
- Do these property tests check anything real?
- My property test failed. Is it a bug or a wrong property?
The agent also reaches for it on its own during test-first work, when a function has an inverse, normalizes or sorts, does arithmetic over money or limits, parses data or holds state. It isn't meant for binary fuzzing, mutation testing, benchmarks or end-to-end UI tests.
- The shape of the function decides whether a property test is worth it and which property to state. Round-trip, idempotence, invariant, order independence, a metamorphic relation, cheap verification, an oracle or determinism. At the very least, "never crashes and rejects bad input with the project's own error". The agent asserts the strongest one the code supports.
- It refuses two kinds of empty tests. A tautology restates the implementation. A vacuous test discards nearly every input in a precondition.
- It proves the property can fail. It plants a bug, watches the test go red and reverts the bug.
- It builds inputs by construction rather than filtering, and generates from the boundary rather than the type. A free-text field gets any string, a webhook gets any JSON. Known boundaries and every production incident become permanent examples. Money is generated as integer minor units.
- It treats event streams as their own case. Any arrival order gives the same state, duplicates change nothing, and a snapshot plus deltas equals the full history. Races between processes aren't property-testable, and the agent says what covers them instead.
- It tests stateful systems against a deliberately simple model.
- It keeps proportion. One property per function that has one, example tests stay, default run counts, and the seed is never fixed for good.
The shape of a test the agent writes, in Python with Hypothesis.
from hypothesis import example, given, strategies as st
@given(st.lists(st.from_regex(r"\d{10}|\d{12}", fullmatch=True), max_size=5))
@example(["7707083893", "500100732259", "7728168971"]) # three IDs in one field, from production
def test_tax_ids_round_trip(ids):
assert parse_tax_ids(format_tax_ids(ids)) == idsBefore trusting it, the agent breaks format_tax_ids on purpose, for example by dropping the last
ID, sees the test fail with a shrunk counterexample, and puts the code back. In the report it
writes "no counterexample in 100 runs over lists of up to five IDs", never "correct for all
inputs".
Each has its own reference that the agent opens only when the case comes up.
| Case | What the reference gives |
|---|---|
| The code seems to have no property | Six rearrangements that expose one |
| A property test failed | How to tell a wrong property from an ambiguous spec or a real bug |
| Reviewing existing property tests | How to find the ones that assert nothing |
The current version sits in metadata.version of property-testing/SKILL.md, so any installed
copy tells you which one it is. Every version has a git tag and a
release with its own archive.
CHANGELOG.md lists what changed.
Repository layout
property-testing/ the skill
SKILL.md the method, then tooling for TypeScript and Python
references/expose-a-property.md when the code seems to have no property
references/triage-a-failure.md when a property test failed
references/review-existing-tests.md when reviewing existing property tests
dist/property-testing.skill the skill folder as a downloadable archive
CHANGELOG.md what changed in each version
README.md, README.ru.md this page in English and in Russian