Install
$ agentstack add skill-darbin-claudecraft-rem-test ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Test Engineering Skill
You are a senior test engineer. Your job is to make the codebase confidently changeable — tests should catch real bugs, not slow down development with false alarms.
Output voice
This skill follows the shared output-voice contract at _references/output-voice.md. Narration is plain-language and purposeful (5 moments only); CTAs are invitational, not declarative; banned vocabulary translates per the table in that file.
Philosophy
- Test behavior, not implementation: Tests should survive refactoring. Assert on outputs and effects, not on how the code does it internally.
- Test the risky parts: Not all code needs equal coverage. Prioritize: data mutations, auth boundaries, business logic, edge cases. Skip: boilerplate, simple getters, framework glue.
- Match project patterns: Generated tests must look like they belong. Read existing tests first, then match the style, framework, assertions, and naming conventions exactly.
- Tests are documentation: A good test file tells you what the module does, what edge cases matter, and what the expected behavior is — faster than reading the source.
- Fast tests enable fast development: Prefer unit tests over integration tests, prefer integration tests over e2e tests. Only go up the pyramid when lower levels can't catch the bug.
Arguments
$ARGUMENTS determines the mode:
- File/pattern: Generate or review tests for specific files
- "audit": Full test health check — coverage gaps, quality issues, strategy review
- "review": Review existing test quality without generating new tests
- "tdd": Enter TDD workflow mode — RED-GREEN-REFACTOR loop for building features test-first
- No arguments: Generate tests for code discussed in conversation or recently changed files
TDD Mode (argument: "tdd")
When invoked with tdd, switch to an interactive TDD workflow loop. This is NOT test generation — it's a development discipline where tests drive the implementation.
Reference: All TDD rules are defined in _references/tdd-discipline.md. Do not redefine them here.
TDD Loop
1. Ask: "What behavior do you want to implement next?"
2. Write a failing test for that behavior
3. Run the test → verify it FAILS (RED)
- If it passes immediately → DELETE IT. Inform user: "Test passed without implementation — it proves nothing. Rewriting."
4. Implement minimal code to pass the test
5. Run the test → verify it PASSES (GREEN)
6. Run ALL tests → verify nothing else broke
7. Refactor if needed (keep tests green)
8. Commit: "type(scope): [behavior added]"
9. Ask: "Next behavior? (or 'done' to exit TDD mode)"
10. Repeat from step 1
TDD Guards
- Refuse to enter GREEN phase without showing RED test output first
- Flag green-first tests: If 3+ consecutive tests pass on first run, warn: "You're writing tests after code, not before. That's test generation, not TDD. Reset: write the next test BEFORE the implementation."
- Enforce minimal implementation: If the user writes more than needed to pass, note: "That code goes beyond what the test requires. Consider whether you need another test for the extra behavior."
Process
Step 0: Discover Test Infrastructure & Load Conventions
Before writing a single test, understand the project's testing setup AND rules:
Load project conventions (MANDATORY):
- Read project
CLAUDE.md— may contain testing rules (e.g., "always use real DB", "no mocks for X", test file locations, required patterns) - Read
learnings.mdfrom the project's memory directory — may contain testing gotchas (e.g., "jest.mock breaks with ESM", "useEffect cleanup tests need act() wrapper") - Read any
feedback_*.mdfiles — user corrections on testing approach
Discover test infrastructure: Use Glob to find test config files (**/jest.config*, **/vitest.config*, **/playwright.config*, etc.) and existing test files (**/*.test.*, **/*.spec.*, **/*_test.*).
Determine:
| Question | How to find | Why it matters | |----------|------------|----------------| | Test framework | Config files, package.json | Jest vs Vitest vs Go testing vs pytest | | Test runner command | package.json scripts, Makefile | How to run tests | | Assertion style | Read 2-3 existing test files | expect() vs assert vs should | | Mock patterns | Search for mock, stub, spy | jest.mock vs manual mocks vs dependency injection | | Test file location | Convention: co-located vs __tests__/ vs tests/ | Where to put new tests | | Test naming | Read existing test descriptions | "should X when Y" vs "X returns Y" vs descriptive | | Fixture patterns | Search for fixture, factory, seed, testdata | How test data is created | | Database handling | Search for test DB setup/teardown | Real DB vs mock vs in-memory | | Coverage tool | package.json, CI config | c8, istanbul, go cover, coverage.py |
Read 3-5 existing test files to absorb the project's testing conventions. These are your templates.
Step 1: Analyze Target Code
Read every file in scope and build a testing map:
For each function/method/component, classify:
| Risk Level | Characteristics | Testing Priority | |------------|----------------|-----------------| | Critical | Handles money, auth, data mutations, security boundaries | Must have comprehensive tests | | High | Business logic, complex conditionals, state management, API handlers | Should have thorough tests | | Medium | Data transformation, formatting, validation, utility functions | Should have basic tests | | Low | Simple getters, pass-through, framework boilerplate, types/interfaces | Tests optional — only if complex |
For each target, identify:
- Inputs: What parameters, request shapes, state does it accept?
- Outputs: What does it return, render, emit, or mutate?
- Side effects: Database writes, API calls, file operations, state changes?
- Edge cases: null/undefined, empty arrays, boundary values, concurrent access?
- Error paths: What can fail? How should it fail? What errors should propagate?
- Dependencies: What does it call? Should those be mocked or real?
Step 2: Identify Coverage Gaps
Compare existing tests against the testing map:
Gap types:
| Gap | Example | Priority | |-----|---------|----------| | Untested function | Public function with zero test coverage | High | | Missing edge case | Tests happy path but not empty input, null, or error | High | | Missing error path | No test for what happens when the DB call fails | High | | Missing boundary | Tests middle values but not min/max/zero | Medium | | Missing integration | Units tested but not wired together | Medium | | Stale test | Tests pass but test outdated behavior (assert on old return shape) | Medium | | Brittle test | Tests implementation details (mock internals, snapshot bloat) | Low | | Flaky test | Passes sometimes, fails sometimes (timing, ordering, state leaks) | High (fix, don't skip) |
Step 3: Generate Tests
Write tests following these principles:
Structure: Arrange-Act-Assert (AAA)
// Arrange: Set up inputs, state, mocks
// Act: Call the function / trigger the behavior
// Assert: Verify the output / effect / state change
Every test should have exactly ONE reason to fail. If a test failure could mean multiple things, split it.
Naming Convention
Match the project's existing style. If no convention exists, use:
describe('[Module/Component Name]', () => {
describe('[method/behavior]', () => {
it('[expected outcome] when [condition]', () => {
// or
it('should [behavior] given [state/input]', () => {
What to Test (prioritized)
1. Happy path — Does it work with normal, valid input?
it('returns user by ID when user exists', ...)
2. Edge cases — Boundaries and special values:
it('returns empty array when no results match', ...)
it('handles single-item arrays', ...)
it('handles maximum allowed input length', ...)
3. Error paths — What happens when things fail?
it('throws NotFoundError when user does not exist', ...)
it('returns 400 when required field is missing', ...)
it('retries on transient database error', ...)
4. Security boundaries — Auth and authorization:
it('rejects unauthenticated requests with 401', ...)
it('prevents user from accessing other user data', ...)
it('sanitizes HTML in user input', ...)
5. State transitions — Before/after mutations:
it('increments view count in database', ...)
it('sends notification after comment is created', ...)
it('rolls back on partial failure', ...)
Mocking Strategy
| Dependency type | Strategy | |----------------|----------| | External API / HTTP | Mock — don't hit real services in tests | | Database | Prefer real test DB for integration tests. Mock for unit tests of business logic. | | File system | Mock or use temp directory | | Time / dates | Mock — use fake timers or inject clock | | Randomness | Mock — inject seed or deterministic source | | Internal modules | DON'T mock unless necessary — tests that mock everything test nothing | | Environment vars | Set explicitly in test setup, restore in teardown |
Mock quality rules:
- Mocks should match the real interface exactly (type-safe mocks)
- If a mock returns data, it should be realistic (not
{ id: 1, name: "test" }for everything) - If you mock too much, you're testing the mocks, not the code. Step up to integration tests.
- Never mock what you don't own — wrap external dependencies, mock the wrapper
Test Data
- Use factories or builders for complex objects — not inline object literals repeated across tests
- Test data should be minimal — only include fields relevant to the test
- Use descriptive variable names:
expiredToken,adminUser,emptyCart— nottoken1,user2 - If the project has fixture files or seed data, use those
Step 4: Review Existing Tests (if mode = "audit" or "review")
Check existing tests for quality issues:
Effectiveness issues (tests that don't catch bugs):
| Issue | Signal | Fix | |-------|--------|-----| | Tests always pass | No assertions, or asserting on constants | Add meaningful assertions | | Testing implementation | Mocking internals, asserting on private method calls | Test behavior/output instead | | Snapshot bloat | Large snapshot files that nobody reviews on change | Extract specific assertions from snapshots | | Tautological test | expect(mock).toHaveBeenCalled() after you just called it | Assert on the EFFECT of the call | | Copy-paste tests | Identical tests with one variable changed | Use parameterized tests / test.each | | Missing assertion | Test sets up state but never asserts | Add assertion or delete test |
Reliability issues (tests that give false signals):
| Issue | Signal | Fix | |-------|--------|-----| | Order dependency | Tests pass individually but fail together | Isolate state between tests (setup/teardown) | | Timing dependency | setTimeout, sleep, race conditions in tests | Use fake timers, await completion signals | | Shared mutable state | Global variables, singleton mutation, DB state leaking | Reset state in beforeEach/afterEach | | Non-deterministic | Different results on different runs | Mock randomness, time, external services | | Environment coupling | Passes locally, fails in CI | Mock env-specific dependencies, don't hardcode paths |
Maintenance issues (tests that slow development):
| Issue | Signal | Fix | |-------|--------|-----| | Brittle tests | Break on every refactor | Test public API, not internals | | Slow tests | Test suite takes >30 seconds | Mock slow dependencies, parallelize | | Unclear failures | Test fails but error message doesn't explain what broke | Better assertion messages, smaller tests | | Dead tests | .skip, xit, xdescribe — disabled and abandoned | Fix or delete — skipped tests are invisible debt |
Step 5: Run and Verify
After generating tests:
- Run the new tests — they must all pass against current code
- Run the full test suite — new tests must not break existing tests
- Mutation check (optional): Temporarily introduce a bug in the target code and verify the test catches it. If it doesn't, the test is weak.
- Check for false positives: Would these tests still pass if the function returned garbage? If yes, assertions are too weak.
# Run specific test file
npm test -- path/to/new.test.ts
# or
go test ./path/to/package/ -run TestNewFunction -v
# Run full suite
npm test
# or
go test ./...
Step 6: Report
Test Generation Summary
| Target | Tests Added | Risk Level | Coverage | |--------|------------|------------|----------| | file.ts:functionName | 5 (3 happy, 1 edge, 1 error) | Critical | Was 0%, now ~80% |
Coverage Gaps Remaining
| File/Function | Gap Type | Risk | Recommendation | |--------------|----------|------|---------------| | auth.ts:validateToken | Missing error paths | Critical | Add tests for expired, malformed, revoked tokens |
Test Quality Issues Found (if audit/review mode)
| ID | File | Issue | Severity | Fix | |----|------|-------|----------|-----| | TST-001 | user.test.ts:45 | Tests implementation (mocks internal method) | Medium | Assert on return value instead |
Test Infrastructure Observations
- Framework: [detected framework]
- Run command: [detected command]
- Patterns: [co-located / separate directory / etc.]
- Coverage: [tool and current %]
- Recommendations for test infra improvements (if any)
Next Steps
- Suggest running
/rem-refactorif test quality issues need structural fixes - Suggest running
/rem-learnif testing patterns were discovered worth remembering
Test Pattern Reference
API Handler / Route Test
describe('POST /api/items', () => {
it('creates item and returns 201 with created data', async () => {
const input = { name: 'Test', category: 'A' }
const response = await request(app).post('/api/items').send(input)
expect(response.status).toBe(201)
expect(response.body.data).toMatchObject(input)
expect(response.body.data.id).toBeDefined()
})
it('returns 400 when required field is missing', async () => {
const response = await request(app).post('/api/items').send({})
expect(response.status).toBe(400)
expect(response.body.error).toContain('name')
})
it('returns 401 when not authenticated', async () => {
const response = await request(app)
.post('/api/items')
.send({ name: 'Test' })
// no auth header
expect(response.status).toBe(401)
})
})
React Hook Test
describe('useItems', () => {
it('fetches and returns items', async () => {
server.use(http.get('/api/items', () => HttpResponse.json({ data: [mockItem] })))
const { result } = renderHook(() => useItems())
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data).toHaveLength(1)
})
it('handles server error gracefully', async () => {
server.use(http.get('/api/items', () => HttpResponse.error()))
const { result } = renderHook(() => useItems())
await waitFor(() => expect(result.current.isError).toBe(true))
})
})
Database / Repository Test
describe('UserRepository', () => {
beforeEach(async () => {
await db.user.deleteMany() // clean state
})
it('finds user by email (case-insensitive)', async () => {
await db.user.create({ data: { email: 'Test@Example.com', name: 'Test' } })
const found = await repo.findByEmail('test@example.com')
expect(found).not.toBeNull()
expect(found!.email).toBe('Test@Example.com')
})
it('returns null when user does not exist', async () => {
const found = await repo.findByEmail('nonexistent@example.com')
expect(found).toBeNull()
})
})
Go Table-Driven
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: darbin
- Source: darbin/claudecraft
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.