Behavior changes require a failing test first
SkillSearchInstills a TDD skill habit in your agent: every bug fix or feature starts with a failing test.
Use Behavior changes require a failing test first in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Behavior changes require a failing test first and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the Behavior changes require a failing test first skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
About this skill
Test-first discipline for Gum. Triggers: behavior changes (bug fix or feature) under GumCommon/, Gum/, MonoGameGum/, RenderingLibrary/, KniGum/, FnaGum/, SkiaGum/, RaylibGum/, Tools/Gum.ProjectServices/. Skip for docs, renames, csproj/projitems plumbing, style-only edits.
What this skill tells your AI
The instructions your AI receives, as published by vchelaru/gum in .claude/skills/tdd/SKILL.md and read by ahel’s review.
Behavior changes in Gum's source projects require a failing unit test in the matching test project before the source edit. Write it, run it via Bash, watch it fail for the right reason, then implement until it goes green.
Test projects to look in (pick the one that compiles the source you're editing):
Tests/Gum.ProjectServices.Tests/— forTools/Gum.ProjectServices/MonoGameGum.Tests/,Tests/MonoGameGum.Tests.V3/— forMonoGameGum/,GumCommon/,RenderingLibrary/Tests/SkiaGum.Tests/— forSkiaGum/Tests/Gum.Presentation.Tests/— forTools/Gum.Presentation/,DataUi.Core/, the plugin cores underTool/*.Core/, and the neutral plugins underGum/(the default for tool logic)Tests/Gum.Avalonia.Tests/— forTool/Gum.Avalonia/andAvaloniaDataUi/(headless Avalonia)Tool/Tests/GumToolUnitTests/— only for the frozen WPF head (Gum/,WpfDataUi/); new tool logic never lands thereTests/Gum.Cli.Tests/— forGum.Cli/Tests/Gum.Bundle.Tests/— forGumCommon/Bundle/(the dependency walker, font reference collector,.gumpkgformat)Tests/Gum.Themes.Tests/— forThemes/
No "the cause is obvious, I'll skip the test" exception — that reasoning is how silent regressions ship. If you're about to edit one of the directories above without a failing test open, stop.
Run the test yourself via Bash. A failure you only reasoned about is not a failure.
Exceptions: docs, csproj/projitems plumbing, pure renames, dead-code removal, cosmetic edits. When in doubt, write the test.
Note: extracting logic into a new class/service/ViewModel is not a pure rename. Even when the move preserves behavior, pin the new unit with a characterization test — see refactoring-direction. The exemption above is for renames and cosmetics, not for relocating logic into a newly-testable seam.
A gesture's spec is a table, agreed before the test
For an input gesture (a click, hotkey, drag or menu action), the failing test asserts an outcome table — gesture × state → where the result lands, which the user has confirmed — not the author's guess at the intent. A test that encodes a guess goes green on behavior the user never asked for; the Standards chip's Ctrl+click was specified as "add at the element root" and tested as such three times before the user's actual rule (every add gesture goes to the same add destination) was written down. When gestures are meant to be the same, they share one handler and one test, so a difference cannot exist.
Make it testable before you decide it can't
Before implementing, decide how the change will be proven — in this order:
- Can it be tested as-is? Then test it: cover the happy path, the negative cases (invalid input is rejected / the expected error is raised), and the edge/boundary cases (null, empty,
0,-1, first/last index, single-element collection, max). - If it's not testable as written, restructure until it is — even code you weren't otherwise here to change. "Can't be tested" is a reason to introduce a seam, not to skip the test. Extract the logic into a class/service that takes its dependencies via the constructor; if a static
.Selfsingleton blocks the seam, drain it on the spot (see CLAUDE.md "Static Singletons" + refactoring-direction for breaking the resulting DI cycle withLazy<T>). Plugin classes that callLocatordirectly are the canonical case: pull the logic into a ctor-injected service, test that, and leave only a thin untested plugin wrapper. - If it's a rendering change with nothing but drawn pixels to assert on (stroke thickness, blend color, clipping), a pixel test that renders offscreen and reads back is the proof, before falling back to manual. SkiaGum uses the golden-image harness in
Tests/SkiaGum.Tests/GoldenImages/(seegum-unit-tests); MonoGame usesTests/MonoGameGum.IntegrationTests/MonoGameGum/Rendering/; raylib usesTests/RaylibGum.Tests/Rendering/. - A tool gesture or wiring change (a click handler, a plugin event reaching a command) is testable end-to-end in
Tests/Gum.Avalonia.Tests:TestAppBuilder.Servicesis the head's real service graph, so a test can create a project, select throughISelectedState, invoke the control's action and assert on theElementSave— seeStandardsPaletteAddTests. Drive the control's action property rather than hosting the head's shared tab content in a test window; that content belongs to the main window in other tests. - A missing seam is never why a behavior goes untested. Native pickers, OS events and framework types you cannot implement get a thin interface of your own in front of them, added in the same PR, and the logic behind it gets its test (
IFilePickersinTool/Gum.Avalonia/Dialogs/FilePickers.csstands in front of Avalonia'sIStorageProvider). Do not defer the seam to a follow-up, and never end with "not covered by tests". - Only what needs a person stays manual: how something looks, or how it feels on a real OS. The PR says so explicitly, with why. (The issue-driven workflow defines that manual step.)
Testability is a gate on the change, not a property you accept as given. Restructuring a blocker into a testable seam is in-scope work, not a separate task.
A new branch is a behavior change — cover it
A cache check, early-return, guard, or "while I'm here" optimization that alters control flow needs its own test — even when it's bolted onto already-working code and isn't the feature you set out to build. These are the classic blind spot: there's no ticket for them, so nobody asks "what covers this?", and a green-only test passes with or without the branch.
Red-first still applies: after adding a branch, remove or invert it and confirm a test goes red. If nothing fails, your change is uncovered — write the test that reaches it. (A real regression shipped exactly this way: a font-loader cache-hit early-return added alongside a feature, reached by no test because every existing font test disabled caching.)
Apply this to the whole diff, not just the branch you set out to add — a refactor that changes how an existing path works (swapping an index lookup for a reference lookup, rerouting a removal) is a changed branch too, and "the full suite is green" only proves the paths the suite already exercised. Before committing, name any branch your diff touched that no test would catch regressing; scoping coverage to the bugs that had a crash-repro is exactly how an untested rework slips through.
Writing the tests
- Quality over coverage. The fewest tests that meaningfully cover the change — 1 ideally, 2–3 only when the feature has genuinely distinct cases. Don't ship near-duplicate tests; combine them or keep the representative one.
- Self-contained arrangements. Every value a test asserts against must be declared in that test's own Arrange section. Shared helpers may do common setup (file creation, object init) but must take the asserted values as parameters — never let a helper define an expected value, or the test breaks silently when the helper changes.
Signals
- GitHub stars
- 631
- Forks
- 83
- Last commit
- Oct 2026
Advanced
- Item type
- skill
- Key
tdd-vchelaru- Source
- github.com/vchelaru/gum