Install
$ agentstack add skill-christopherlouet-claude-base-dev-tdd ✓ 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 No
- ✓ 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-Driven Development (TDD)
Iron Law
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
If code was written before the test: delete it. Start over with TDD.
- Don't keep it "as a reference"
- Don't "adapt" it by writing the tests
- Don't look at it
- Delete = delete
Implement from scratch starting from the tests. Period.
TDD Cycle
┌─────────┐ ┌─────────┐ ┌──────────┐
│ RED │ ──▶ │ GREEN │ ──▶ │ REFACTOR │
│ Test │ │ Code │ │ Clean │
│ fail │ │ pass │ │ up │
└─────────┘ └─────────┘ └──────────┘
▲ │
└──────────────────────────────┘
Phase 1: RED - Write a failing test
Write ONE minimal test showing the expected behavior
describe('Module', () => {
describe('function', () => {
it('should [behavior] when [condition]', () => {
// Arrange - Prepare
// Act - Execute
// Assert - Verify
});
});
});
Good test vs Bad test
Good: Clear name, tests real behavior, one thing only
test('retries failed operations 3 times', async () => {
let attempts = 0;
const operation = () => {
attempts++;
if (attempts {
const mock = jest.fn()
.mockRejectedValueOnce(new Error())
.mockRejectedValueOnce(new Error())
.mockResolvedValueOnce('success');
await retryOperation(mock);
expect(mock).toHaveBeenCalledTimes(3);
});
Verify RED (MANDATORY - never skip)
npm test path/to/test.test.ts
Confirm:
- The test fails (no syntax error)
- The failure message is the expected one
- The failure comes from the missing feature (not a typo)
Test passes immediately? You're testing existing behavior. Fix the test.
Test has a syntax error? Fix it, rerun until you get a proper failure.
Phase 2: GREEN - Minimal code
Write the simplest code to pass the test. Nothing more.
Good: Just enough to pass
async function retryOperation(fn: () => Promise): Promise {
for (let i = 0; i (
fn: () => Promise,
options?: {
maxRetries?: number;
backoff?: 'linear' | 'exponential';
onRetry?: (attempt: number) => void;
}
): Promise {
// Features not requested by a test
}
Don't add features, refactor other code, or "improve" beyond the test.
Verify GREEN (MANDATORY)
npm test path/to/test.test.ts
Confirm:
- The test passes
- Other tests still pass
- Clean output (no errors, warnings)
Test fails? Fix the code, not the test.
Other tests fail? Fix them now.
Phase 3: REFACTOR - Clean up
After GREEN only:
- Remove duplications
- Improve names
- Extract helpers
Keep tests green. Don't add behavior.
Commit
git commit -m "test(scope): add tests for [feature]"
git commit -m "feat(scope): implement [feature]"
Then start over: next failing test for the next feature.
Why the order matters
"I'll write the tests after to verify"
Tests written after the code pass immediately. Passing immediately proves nothing:
- The test may be testing the wrong thing
- The test may be testing the implementation instead of the behavior
- The test may miss forgotten edge cases
- You never saw the test catch the bug
Test-first forces you to see the test fail, proving it tests something.
"I already manually tested all the cases"
Manual testing is ad-hoc:
- No record of what was tested
- Impossible to rerun when the code changes
- Easy to forget cases under pressure
- "It worked when I tried" ≠ complete test
Automated tests are systematic. They run the same way every time.
"Deleting X hours of work is a waste"
Sunk cost fallacy. The time is already lost. The choice now:
- Delete and rewrite in TDD (X more hours, high confidence)
- Keep and add tests after (30 min, low confidence, likely bugs)
The "waste" is keeping code you can't trust.
Common rationalizations
| Excuse | Reality | |--------|---------| | "Too simple to test" | Simple code breaks. The test takes 30 seconds. | | "I'll write the tests after" | Tests that pass immediately prove nothing. | | "Tests-after reach the same goal" | Tests-after = "what does it do?" Tests-first = "what should it do?" | | "I already manually tested" | Ad-hoc ≠ systematic. No record, not replayable. | | "Deleting X hours of work is a waste" | Sunk cost. Keeping unverified code = technical debt. | | "I keep it as a reference and write the tests first" | You'll adapt it. It's disguised test-after. Delete = delete. | | "I need to explore first" | OK. Throw away the exploration, start in TDD. | | "It's hard to test = unclear design" | Listen to the test. Hard to test = hard to use. | | "TDD will slow me down" | TDD faster than debugging. Pragmatic = test-first. | | "Manual testing is faster" | Manual testing doesn't prove edge cases. You retest on every change. | | "Existing code has no tests" | We're improving it. Add tests for the existing code. | | "It's different because..." | No. No exception without explicit user permission. |
Red Flags — STOP and start over
Stop immediately if you find yourself:
- Writing code before the test
- Writing the test after the implementation
- A test that passes immediately
- Unable to explain why the test failed
- Adding tests "later"
- Rationalizing "just this once"
- "I already manually tested"
- "Tests-after reach the same goal"
- "It's the spirit that counts, not the ritual"
- "I keep it as a reference" or "I adapt the existing code"
- "I already spent X hours, deleting is a waste"
- "TDD is dogmatic, I'm pragmatic"
- "It's different because..."
All these signals mean: delete the code. Start over in TDD.
Qualities of a good test
| Quality | Good | Bad | |---------|-----|---------| | Minimal | One thing only. "and" in the name? Split it. | test('validates email and domain and whitespace') | | Clear | The name describes the behavior | test('test1') | | Intentional | Demonstrates the desired API | Obscures what the code should do |
Complete example: Bug Fix
Bug: Empty email accepted
RED
test('rejects empty email', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('Email required');
});
Verify RED
$ npm test
FAIL: expected 'Email required', got undefined
GREEN
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: 'Email required' };
}
// ...
}
Verify GREEN
$ npm test
PASS
REFACTOR: Extract the validation for other fields if necessary.
Verification checklist
Before declaring the work done:
- [ ] Each new function/method has a test
- [ ] Each test was seen failing before implementing
- [ ] Each test failed for the right reason (missing feature, not a typo)
- [ ] Minimal code written to pass each test
- [ ] All tests pass
- [ ] Clean output (no errors, warnings)
- [ ] Tests on real code (mocks only if unavoidable)
- [ ] Edge cases and errors covered
Can't check all boxes? TDD was skipped. Start over.
When you're stuck
| Problem | Solution | |----------|----------| | Don't know how to test | Write the desired API. Write the assertion first. Ask the user. | | Test too complicated | Design too complicated. Simplify the interface. | | Everything must be mocked | Code too coupled. Use dependency injection. | | Huge test setup | Extract helpers. Still complex? Simplify the design. |
Useful commands
# Run the tests
npm test
# Tests in watch mode
npm run test:watch
# With coverage
npm run test:coverage
# A specific file
npm test -- --grep "test name"
Generating tests for existing code
TDD is test-first, but the same discipline applies when back-filling tests on code that already exists (legacy, coverage gaps).
- Analyze the target: public functions, dependencies, conditional branches, side effects.
- Enumerate cases by category:
- Nominal (happy path) — valid inputs, expected behavior.
- Edge —
null,undefined,"",[],{},0,-1,MAX_INT, empty/very long strings. - Errors — invalid inputs, expected exceptions, impossible states.
- Boundary — off-by-one, thresholds (just before / exactly / just after), state transitions.
- Write in AAA structure (Arrange-Act-Assert) with descriptive names.
- Verify — run, then check coverage: critical business logic 90%+, services/utils 80%+, UI 70%+.
Same rules as test-first: no mocks except external deps, tests independent and order-free.
Test infrastructure setup
When a project has no test setup yet:
- Framework matched to the stack: Vitest (React/Vue/Node), Pytest (Python),
go test(Go). - Coverage thresholds configured (new code 80%, critical 90%, utils 100%, UI 70%).
- Mocks: MSW for API mocks (preferred over manual mocks); never mock what can be tested for real (pure functions, utils).
- Layout: co-located or
__tests__; global setup + shared mocks. - npm scripts:
test,test:watch,test:ui,test:coverage,test:ci. - CI/CD: GitHub Actions job running the suite with coverage upload (
/ops:ops-ci).
Rules
- NEVER write the code before the tests
- A test that passes from the start is a BAD test
- Cover edge cases (null, undefined, empty, limits)
- Mocks ONLY for external dependencies (API, DB, filesystem)
- NEVER modify a test to make it pass — fix the implementation
- Each test MUST be seen failing before writing the code
- Delete code written without a test. No exception.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: christopherlouet
- Source: christopherlouet/claude-base
- License: MIT
- Homepage: https://christopherlouet.github.io/claude-base/
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.