Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cursor/rules/learnings-index.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ propose a retirement, a consolidation, or a glob-scoped sub-index split.
class they silently assert nothing.
→ .agents/skills/gem-rspock--rspock/ (ships in the rspock gem; linked by
`dev up` / `dev install-deps`)
- [testing/nested-test-fakes] Test fake classes nest inside the test class
they serve — never at file top level with prefixed names and
`unless defined?` guards.
→ .cursor/skills/learnings/nested-test-fakes/

## process

Expand Down
40 changes: 40 additions & 0 deletions .cursor/skills/learnings/nested-test-fakes/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: nested-test-fakes
description: >-
MUST be used when a test needs a fake or stub class (e.g. a
Dev::BuiltinCommand fake): nest it inside the test class it serves,
never at file top level.
---

# Test fakes nest inside their test class

A fake class belongs inside the test class that uses it: the constant is
namespaced (`Dev::FooTest::FakeBuiltin`), so cross-file collisions are
impossible and the `unless defined?` guard plus unique name prefixes
(`ServiceFakeBuiltin`, `DispatchFakeBuiltin`, …) become dead weight.
Nesting inside an rspock `transform!`-ed class is safe — the
transformation only rewrites `test "..." do` blocks (plain nested classes
merely gain a harmless `extend RSpock::Declarative`).

Wrong — top-level fake with prefix and guard:

class ServiceFakeBuiltin < Dev::BuiltinCommand
def desc = "a builtin"
def call(args:, context:); end
end unless defined?(ServiceFakeBuiltin)

transform!(RSpock::AST::Transformation)
class Dev::CommandServiceTest < Minitest::Test

Right — nested, short name, no guard:

transform!(RSpock::AST::Transformation)
class Dev::CommandServiceTest < Minitest::Test
class FakeBuiltin < Dev::BuiltinCommand
def desc = "a builtin"
def call(args:, context:); end
end

learned-from: dev#122 review (threads on builtin_executor_test.rb and
overridden_executor_test.rb: "move the fake to be inside the test class")
date: 2026-08-18
Loading