$npx -y skills add hegeldev/hegel-skill --skill hegelWrite property-based tests using Hegel across Rust, Go, C++, and TypeScript projects. Use this skill whenever the user asks to write tests, add test coverage, or improve testing for functions, modules, or libraries — especially when the code has properties like round-trips, invar
| 1 | # Hegel: Property-Based Testing |
| 2 | |
| 3 | Hegel is a family of property-based testing libraries supporting multiple languages, powered by Hypothesis. Tests integrate with standard language test runners. Hegel generates random inputs for your code and automatically shrinks failing cases to minimal counterexamples. |
| 4 | |
| 5 | Even when PBTs add modest line coverage over unit tests, their value is in exercising combinations and boundary conditions that humans don't think to write by hand. |
| 6 | |
| 7 | **Code examples in this file use Python-like pseudocode to illustrate concepts.** For exact API and syntax, load the language-specific reference (see step 1 of the workflow). |
| 8 | |
| 9 | ## Workflow |
| 10 | |
| 11 | Follow these steps when writing property-based tests. |
| 12 | |
| 13 | ### 1. Load the Language Reference |
| 14 | |
| 15 | Determine the project language and load the corresponding reference from `references/<language>/reference.md` for API details and idiomatic patterns. |
| 16 | |
| 17 | ### 2. Explore the Code Under Test |
| 18 | |
| 19 | Before writing any test, understand what you're testing: |
| 20 | |
| 21 | - **Read the source code** of the function/module under test |
| 22 | - **Read existing tests** to understand expected behavior and edge cases |
| 23 | - **Read docstrings, comments, and type signatures** for documented contracts |
| 24 | - **Read usage sites** to see how callers use the code and what they expect |
| 25 | |
| 26 | The goal is to find *evidence* for properties, not to invent them. |
| 27 | |
| 28 | ### 3. Identify Valuable Properties |
| 29 | |
| 30 | Look for properties that are: |
| 31 | |
| 32 | - **Grounded in evidence** from the code, docs, or usage patterns |
| 33 | - **Non-trivial** — they test real behavior, not tautologies, and do not duplicate the code being tested |
| 34 | - **Falsifiable** — a buggy implementation could actually violate them |
| 35 | |
| 36 | Write one test per property. Don't cram multiple properties into one test. |
| 37 | |
| 38 | See the **Property Catalogue** below for a taxonomy of what to look for. |
| 39 | |
| 40 | ### 4. Check for Existing Tests to Evolve or Port |
| 41 | |
| 42 | Before writing tests from scratch, check what already exists. |
| 43 | |
| 44 | **Existing PBTs in another framework** (proptest, quickcheck, rapid, gopter, etc.) should be ported to hegel. Load the language-specific porting reference (`references/<language>/porting.md`). Key things to know about hegel when porting: |
| 45 | |
| 46 | - **Hegel is imperative.** Most PBT libraries declare what to generate in a function signature or strategy combinator. In hegel, your test receives a test case handle and calls `tc.draw()` whenever it needs a value — you can draw conditionally, in loops, and have later draws depend on earlier values without needing `flat_map`. |
| 47 | - **Shrinking is automatic.** Hegel's shrinking is handled server-side by Hypothesis. You don't implement shrink logic or define shrinking strategies. |
| 48 | - **Standard assertions.** Use the language's normal assertion mechanism. No special `prop_assert!` or return-a-bool pattern needed. |
| 49 | - **Broaden your generators.** Many existing PBTs use narrow input ranges because shrinking was slow or unreliable. Hegel's shrinking is more robust — try broader generators than the originals. |
| 50 | |
| 51 | **Unit tests and example-based tests** can often be evolved into PBTs. Tests with hardcoded seeds, parameterized examples, or multiple similar test cases are prime candidates. Load `references/evolving-tests.md` for detailed guidance on recognizing what property a unit test is hiding. If you can't immediately see the right property, start by parameterizing the test — replace concrete values with generated ones and keep a simple oracle. You can refine the property later. |
| 52 | |
| 53 | **Tests that use `rand` with fixed seeds** are especially good candidates — the randomness should come from hegel instead so failures produce shrinkable counterexamples. |
| 54 | |
| 55 | When you evolve an existing test, **modify the existing test file** rather than creating a new one. Property-based tests are tests like any other and belong with the code they're testing. Do not create a separate file for hegel tests. |
| 56 | |
| 57 | ### 5. Write the Tests |
| 58 | |
| 59 | For each property: |
| 60 | |
| 61 | 1. **Add tests to the appropriate existing test file.** Only create a new file if no relevant test file exists. |
| 62 | 2. Choose the **simplest possible generators** — see Generator Discipline below. |
| 63 | 3. Draw values, run the code under test, and assert the property. |
| 64 | |
| 65 | ### 6. Run and Reflect |
| 66 | |
| 67 | Run the tests. When a test fails, ask: |
| 68 | |
| 69 | - **Is this a real bug?** If the code violates its own contract, flag the bug to the user and ask what to do, or fix the code if instructed to do so. |
| 70 | - |