moicle 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/assets/architecture/go-backend.md +930 -108
- package/assets/commands/brainstorm.md +1 -0
- package/assets/skills/api-integration/SKILL.md +883 -0
- package/assets/skills/deprecation/SKILL.md +923 -0
- package/assets/skills/documentation/SKILL.md +1333 -0
- package/assets/skills/fix-pr-comment/SKILL.md +283 -0
- package/assets/skills/go-module/SKILL.md +77 -0
- package/assets/skills/incident-response/SKILL.md +946 -0
- package/assets/skills/onboarding/SKILL.md +607 -0
- package/assets/skills/pr-review/SKILL.md +620 -0
- package/assets/skills/refactor/SKILL.md +756 -0
- package/assets/skills/spike/SKILL.md +535 -0
- package/assets/skills/tdd/SKILL.md +828 -0
- package/bin/cli.js +2 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +20 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/utils/symlink.d.ts +1 -0
- package/dist/utils/symlink.d.ts.map +1 -1
- package/dist/utils/symlink.js +1 -0
- package/dist/utils/symlink.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd
|
|
3
|
+
description: Test-Driven Development workflow. Use when doing TDD, writing tests first, or when user says "tdd", "test first", "test driven", "red green refactor".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test-Driven Development (TDD) Workflow
|
|
7
|
+
|
|
8
|
+
Red-Green-Refactor cycle for test-first development with quality gates.
|
|
9
|
+
|
|
10
|
+
## IMPORTANT: Read Architecture First
|
|
11
|
+
|
|
12
|
+
**Before starting TDD, you MUST read the appropriate architecture reference:**
|
|
13
|
+
|
|
14
|
+
### Global Architecture Files
|
|
15
|
+
```
|
|
16
|
+
~/.claude/architecture/
|
|
17
|
+
├── clean-architecture.md # Core principles for all projects
|
|
18
|
+
├── flutter-mobile.md # Flutter + Riverpod
|
|
19
|
+
├── react-frontend.md # React + Vite + TypeScript
|
|
20
|
+
├── go-backend.md # Go + Gin
|
|
21
|
+
├── laravel-backend.md # Laravel + PHP
|
|
22
|
+
├── remix-fullstack.md # Remix fullstack
|
|
23
|
+
└── monorepo.md # Monorepo structure
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Project-specific (if exists)
|
|
27
|
+
```
|
|
28
|
+
.claude/architecture/ # Project overrides
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Understand the test patterns and conventions defined in these files.**
|
|
32
|
+
|
|
33
|
+
## Recommended Agents
|
|
34
|
+
|
|
35
|
+
| Phase | Agent | Purpose |
|
|
36
|
+
|-------|-------|---------|
|
|
37
|
+
| RED | `@test-writer` | Write failing tests first |
|
|
38
|
+
| GREEN | `@react-frontend-dev`, `@go-backend-dev`, `@laravel-backend-dev`, `@flutter-mobile-dev`, `@remix-fullstack-dev` | Minimal implementation |
|
|
39
|
+
| REFACTOR | `@refactor` | Clean up code |
|
|
40
|
+
| REFACTOR | `@code-reviewer` | Review refactored code |
|
|
41
|
+
| REFACTOR | `@perf-optimizer` | Performance optimization |
|
|
42
|
+
|
|
43
|
+
## Workflow Overview
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌─────────────────────────────────┐
|
|
47
|
+
│ TDD CYCLE │
|
|
48
|
+
│ │
|
|
49
|
+
│ ┌──────┐ ┌───────┐ ┌─────────┐
|
|
50
|
+
│ │ RED │──▶│ GREEN │──▶│REFACTOR │
|
|
51
|
+
│ └──────┘ └───────┘ └─────────┘
|
|
52
|
+
│ ▲ │
|
|
53
|
+
│ │ │
|
|
54
|
+
│ └─────────────────────────┘
|
|
55
|
+
│ Next requirement
|
|
56
|
+
└─────────────────────────────────┘
|
|
57
|
+
|
|
58
|
+
RED: Write a failing test
|
|
59
|
+
GREEN: Make it pass (minimal code)
|
|
60
|
+
REFACTOR: Improve code (keep tests green)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Phase 1: RED - Write Failing Test
|
|
66
|
+
|
|
67
|
+
**Goal**: Write a test that fails because the feature doesn't exist yet
|
|
68
|
+
|
|
69
|
+
### Actions
|
|
70
|
+
|
|
71
|
+
1. **Read architecture doc** for test conventions:
|
|
72
|
+
- Test file location pattern
|
|
73
|
+
- Test framework/runner
|
|
74
|
+
- Mocking patterns
|
|
75
|
+
- Naming conventions
|
|
76
|
+
|
|
77
|
+
2. **Understand the requirement**:
|
|
78
|
+
- What should this code do?
|
|
79
|
+
- What are the inputs?
|
|
80
|
+
- What are the expected outputs?
|
|
81
|
+
- What are the edge cases?
|
|
82
|
+
|
|
83
|
+
3. **Write a single failing test**:
|
|
84
|
+
```
|
|
85
|
+
// Start with ONE test for ONE behavior
|
|
86
|
+
// Don't write multiple tests at once
|
|
87
|
+
// Follow test patterns from architecture doc
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
4. **Run the test and verify it fails**:
|
|
91
|
+
- Test MUST fail for the right reason
|
|
92
|
+
- Failure message should be clear
|
|
93
|
+
- If test passes unexpectedly → investigate
|
|
94
|
+
|
|
95
|
+
### Test Patterns by Stack
|
|
96
|
+
|
|
97
|
+
#### Flutter
|
|
98
|
+
```dart
|
|
99
|
+
// test/[feature]_test.dart
|
|
100
|
+
import 'package:flutter_test/flutter_test.dart';
|
|
101
|
+
|
|
102
|
+
void main() {
|
|
103
|
+
group('[Feature]', () {
|
|
104
|
+
test('should [expected behavior]', () {
|
|
105
|
+
// Arrange
|
|
106
|
+
|
|
107
|
+
// Act
|
|
108
|
+
|
|
109
|
+
// Assert
|
|
110
|
+
expect(actual, expected);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### React/TypeScript
|
|
117
|
+
```typescript
|
|
118
|
+
// src/[feature].test.ts
|
|
119
|
+
import { describe, it, expect } from 'vitest';
|
|
120
|
+
|
|
121
|
+
describe('[Feature]', () => {
|
|
122
|
+
it('should [expected behavior]', () => {
|
|
123
|
+
// Arrange
|
|
124
|
+
|
|
125
|
+
// Act
|
|
126
|
+
|
|
127
|
+
// Assert
|
|
128
|
+
expect(actual).toBe(expected);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Go
|
|
134
|
+
```go
|
|
135
|
+
// [package]_test.go
|
|
136
|
+
package mypackage
|
|
137
|
+
|
|
138
|
+
import "testing"
|
|
139
|
+
|
|
140
|
+
func Test[Feature](t *testing.T) {
|
|
141
|
+
// Arrange
|
|
142
|
+
|
|
143
|
+
// Act
|
|
144
|
+
|
|
145
|
+
// Assert
|
|
146
|
+
if got != want {
|
|
147
|
+
t.Errorf("got %v, want %v", got, want)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Laravel/PHP
|
|
153
|
+
```php
|
|
154
|
+
// tests/Unit/[Feature]Test.php
|
|
155
|
+
namespace Tests\Unit;
|
|
156
|
+
|
|
157
|
+
use PHPUnit\Framework\TestCase;
|
|
158
|
+
|
|
159
|
+
class FeatureTest extends TestCase
|
|
160
|
+
{
|
|
161
|
+
public function test_should_expected_behavior()
|
|
162
|
+
{
|
|
163
|
+
// Arrange
|
|
164
|
+
|
|
165
|
+
// Act
|
|
166
|
+
|
|
167
|
+
// Assert
|
|
168
|
+
$this->assertEquals($expected, $actual);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Output
|
|
174
|
+
```markdown
|
|
175
|
+
## RED Phase
|
|
176
|
+
|
|
177
|
+
### Test Written
|
|
178
|
+
**File**: [path to test file]
|
|
179
|
+
**Test Name**: `test_[behavior]`
|
|
180
|
+
|
|
181
|
+
### Test Code
|
|
182
|
+
[code snippet]
|
|
183
|
+
|
|
184
|
+
### Expected Failure
|
|
185
|
+
**Reason**: [why it should fail - feature not implemented]
|
|
186
|
+
|
|
187
|
+
### Test Run Result
|
|
188
|
+
```bash
|
|
189
|
+
[test command from architecture doc]
|
|
190
|
+
[failure output]
|
|
191
|
+
```
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Gate
|
|
195
|
+
- [ ] Architecture doc read for test patterns
|
|
196
|
+
- [ ] Single test written (AAA pattern)
|
|
197
|
+
- [ ] Test follows architecture conventions
|
|
198
|
+
- [ ] Test fails for the RIGHT reason
|
|
199
|
+
- [ ] Failure message is clear
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Phase 2: GREEN - Make It Pass
|
|
204
|
+
|
|
205
|
+
**Goal**: Write the minimal code to make the test pass (no more, no less)
|
|
206
|
+
|
|
207
|
+
### Principles
|
|
208
|
+
|
|
209
|
+
1. **Minimal code** - Write only enough to pass the test
|
|
210
|
+
2. **No gold-plating** - Don't add features not tested
|
|
211
|
+
3. **Simple first** - Hardcode if needed, refactor later
|
|
212
|
+
4. **Follow architecture** - Respect layer boundaries from doc
|
|
213
|
+
|
|
214
|
+
### Actions
|
|
215
|
+
|
|
216
|
+
1. **Read architecture doc** for implementation patterns:
|
|
217
|
+
- Where to create the file
|
|
218
|
+
- Naming conventions
|
|
219
|
+
- Layer boundaries
|
|
220
|
+
- Dependency injection patterns
|
|
221
|
+
|
|
222
|
+
2. **Write minimal implementation**:
|
|
223
|
+
```
|
|
224
|
+
// Quick and dirty is OK at this stage
|
|
225
|
+
// Even hardcoded values are fine if test passes
|
|
226
|
+
// Focus on making test green, not perfect code
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
3. **Run the test**:
|
|
230
|
+
```bash
|
|
231
|
+
# Use test command from architecture doc
|
|
232
|
+
flutter test # Flutter
|
|
233
|
+
go test ./... # Go
|
|
234
|
+
bun test # React/Remix
|
|
235
|
+
php artisan test # Laravel
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
4. **Verify test passes**:
|
|
239
|
+
- Test goes from RED → GREEN
|
|
240
|
+
- No other tests broken
|
|
241
|
+
- All tests in suite still pass
|
|
242
|
+
|
|
243
|
+
### Implementation Examples
|
|
244
|
+
|
|
245
|
+
#### Start Simple
|
|
246
|
+
```typescript
|
|
247
|
+
// ❌ Don't write this first:
|
|
248
|
+
function calculate(a: number, b: number, operation: string): number {
|
|
249
|
+
switch(operation) {
|
|
250
|
+
case 'add': return a + b;
|
|
251
|
+
case 'subtract': return a - b;
|
|
252
|
+
case 'multiply': return a * b;
|
|
253
|
+
default: throw new Error('Invalid operation');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ✅ Do write this first (if test only checks addition):
|
|
258
|
+
function calculate(a: number, b: number): number {
|
|
259
|
+
return a + b; // Just make test pass
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Output
|
|
264
|
+
```markdown
|
|
265
|
+
## GREEN Phase
|
|
266
|
+
|
|
267
|
+
### Implementation
|
|
268
|
+
**File**: [path to implementation file]
|
|
269
|
+
**Architecture Reference**: [layer from doc]
|
|
270
|
+
|
|
271
|
+
### Code Written
|
|
272
|
+
[minimal code snippet]
|
|
273
|
+
|
|
274
|
+
### Test Run Result
|
|
275
|
+
```bash
|
|
276
|
+
[test command]
|
|
277
|
+
✓ All tests pass
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Verification
|
|
281
|
+
- [ ] Target test passes
|
|
282
|
+
- [ ] No existing tests broken
|
|
283
|
+
- [ ] Code follows architecture doc structure
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Gate
|
|
287
|
+
- [ ] Test passes (RED → GREEN)
|
|
288
|
+
- [ ] Minimal code written (no extras)
|
|
289
|
+
- [ ] Architecture boundaries respected
|
|
290
|
+
- [ ] All tests in suite pass
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Phase 3: REFACTOR - Improve Code
|
|
295
|
+
|
|
296
|
+
**Goal**: Clean up code while keeping all tests green
|
|
297
|
+
|
|
298
|
+
### Principles
|
|
299
|
+
|
|
300
|
+
1. **Keep tests green** - Run tests after each refactor
|
|
301
|
+
2. **Small steps** - One refactor at a time
|
|
302
|
+
3. **Follow architecture** - Improve architecture compliance
|
|
303
|
+
4. **No new functionality** - Tests stay same
|
|
304
|
+
|
|
305
|
+
### Refactoring Checklist
|
|
306
|
+
|
|
307
|
+
#### Code Quality
|
|
308
|
+
- [ ] Remove duplication (DRY)
|
|
309
|
+
- [ ] Improve naming (clear, descriptive)
|
|
310
|
+
- [ ] Extract methods/functions (SRP)
|
|
311
|
+
- [ ] Simplify logic (reduce complexity)
|
|
312
|
+
- [ ] Remove magic numbers/strings
|
|
313
|
+
|
|
314
|
+
#### Architecture Compliance
|
|
315
|
+
- [ ] Follow patterns from architecture doc
|
|
316
|
+
- [ ] Respect layer boundaries
|
|
317
|
+
- [ ] Use dependency injection (if in doc)
|
|
318
|
+
- [ ] Follow naming conventions from doc
|
|
319
|
+
- [ ] Match directory structure from doc
|
|
320
|
+
|
|
321
|
+
#### Test Quality
|
|
322
|
+
- [ ] Remove test duplication
|
|
323
|
+
- [ ] Improve test names
|
|
324
|
+
- [ ] Add test helpers (if needed)
|
|
325
|
+
- [ ] Follow test patterns from doc
|
|
326
|
+
|
|
327
|
+
### Actions
|
|
328
|
+
|
|
329
|
+
1. **Read architecture doc** for refactoring patterns
|
|
330
|
+
|
|
331
|
+
2. **Identify refactoring opportunities**:
|
|
332
|
+
```
|
|
333
|
+
- Duplicated code?
|
|
334
|
+
- Poor names?
|
|
335
|
+
- Complex logic?
|
|
336
|
+
- Architecture violations?
|
|
337
|
+
- Performance issues?
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
3. **Refactor in small steps**:
|
|
341
|
+
```
|
|
342
|
+
1. Make one small change
|
|
343
|
+
2. Run tests → must stay GREEN
|
|
344
|
+
3. Commit (optional)
|
|
345
|
+
4. Repeat
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
4. **Run full test suite after each change**:
|
|
349
|
+
```bash
|
|
350
|
+
# After EVERY refactor
|
|
351
|
+
flutter test # Flutter
|
|
352
|
+
go test ./... # Go
|
|
353
|
+
bun test # React/Remix
|
|
354
|
+
php artisan test # Laravel
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Refactoring Patterns
|
|
358
|
+
|
|
359
|
+
#### Extract Method
|
|
360
|
+
```typescript
|
|
361
|
+
// Before
|
|
362
|
+
function process(data: Data): Result {
|
|
363
|
+
const cleaned = data.trim().toLowerCase();
|
|
364
|
+
const validated = cleaned.length > 0 && cleaned.length < 100;
|
|
365
|
+
if (!validated) throw new Error('Invalid');
|
|
366
|
+
return { value: cleaned };
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// After
|
|
370
|
+
function process(data: Data): Result {
|
|
371
|
+
const cleaned = cleanData(data);
|
|
372
|
+
validateData(cleaned);
|
|
373
|
+
return createResult(cleaned);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function cleanData(data: Data): string {
|
|
377
|
+
return data.trim().toLowerCase();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function validateData(data: string): void {
|
|
381
|
+
if (data.length === 0 || data.length >= 100) {
|
|
382
|
+
throw new Error('Invalid');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function createResult(value: string): Result {
|
|
387
|
+
return { value };
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### Remove Duplication
|
|
392
|
+
```go
|
|
393
|
+
// Before
|
|
394
|
+
func Add(a, b int) int {
|
|
395
|
+
if a < 0 { return 0 }
|
|
396
|
+
if b < 0 { return 0 }
|
|
397
|
+
return a + b
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
func Subtract(a, b int) int {
|
|
401
|
+
if a < 0 { return 0 }
|
|
402
|
+
if b < 0 { return 0 }
|
|
403
|
+
return a - b
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// After
|
|
407
|
+
func Add(a, b int) int {
|
|
408
|
+
return calculate(a, b, func(x, y int) int { return x + y })
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
func Subtract(a, b int) int {
|
|
412
|
+
return calculate(a, b, func(x, y int) int { return x - y })
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
func calculate(a, b int, op func(int, int) int) int {
|
|
416
|
+
if a < 0 || b < 0 { return 0 }
|
|
417
|
+
return op(a, b)
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
#### Improve Architecture Compliance
|
|
422
|
+
```typescript
|
|
423
|
+
// Before (violates layer separation)
|
|
424
|
+
// ui/UserProfile.tsx
|
|
425
|
+
function UserProfile() {
|
|
426
|
+
const user = fetch('/api/users/1').then(r => r.json()); // ❌ Direct API call
|
|
427
|
+
return <div>{user.name}</div>;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// After (follows architecture doc)
|
|
431
|
+
// ui/UserProfile.tsx
|
|
432
|
+
function UserProfile() {
|
|
433
|
+
const user = useUserRepository().getUser(1); // ✅ Uses repository layer
|
|
434
|
+
return <div>{user.name}</div>;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// data/repositories/UserRepository.ts
|
|
438
|
+
export function useUserRepository() {
|
|
439
|
+
return {
|
|
440
|
+
getUser: (id: number) => fetch(`/api/users/${id}`).then(r => r.json())
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Output
|
|
446
|
+
```markdown
|
|
447
|
+
## REFACTOR Phase
|
|
448
|
+
|
|
449
|
+
### Refactorings Applied
|
|
450
|
+
|
|
451
|
+
1. **[Refactoring Name]**
|
|
452
|
+
- Before: [description/code]
|
|
453
|
+
- After: [description/code]
|
|
454
|
+
- Reason: [why this improves code]
|
|
455
|
+
- Architecture compliance: [how it follows doc]
|
|
456
|
+
|
|
457
|
+
2. **[Refactoring Name]**
|
|
458
|
+
- Before: [description/code]
|
|
459
|
+
- After: [description/code]
|
|
460
|
+
- Reason: [why this improves code]
|
|
461
|
+
|
|
462
|
+
### Test Run After Each Refactor
|
|
463
|
+
```bash
|
|
464
|
+
[test command]
|
|
465
|
+
✓ All tests still pass
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Architecture Compliance
|
|
469
|
+
- [ ] Follows patterns from [architecture doc]
|
|
470
|
+
- [ ] Layer boundaries respected
|
|
471
|
+
- [ ] Naming conventions followed
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Gate
|
|
475
|
+
- [ ] Code improved (cleaner, clearer)
|
|
476
|
+
- [ ] Architecture compliance improved
|
|
477
|
+
- [ ] All tests still pass (GREEN)
|
|
478
|
+
- [ ] No new functionality added
|
|
479
|
+
- [ ] Committed (optional)
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## TDD Best Practices
|
|
484
|
+
|
|
485
|
+
### Test Quality
|
|
486
|
+
|
|
487
|
+
#### Good Test Names
|
|
488
|
+
```
|
|
489
|
+
✅ test_should_return_empty_list_when_no_items_found
|
|
490
|
+
✅ test_should_throw_error_when_invalid_email
|
|
491
|
+
✅ test_should_calculate_total_with_tax
|
|
492
|
+
|
|
493
|
+
❌ test_user
|
|
494
|
+
❌ test1
|
|
495
|
+
❌ test_it_works
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
#### AAA Pattern (Arrange-Act-Assert)
|
|
499
|
+
```typescript
|
|
500
|
+
test('should calculate discount', () => {
|
|
501
|
+
// Arrange - Setup
|
|
502
|
+
const price = 100;
|
|
503
|
+
const discountPercent = 10;
|
|
504
|
+
|
|
505
|
+
// Act - Execute
|
|
506
|
+
const result = calculateDiscount(price, discountPercent);
|
|
507
|
+
|
|
508
|
+
// Assert - Verify
|
|
509
|
+
expect(result).toBe(90);
|
|
510
|
+
});
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### Test One Thing
|
|
514
|
+
```typescript
|
|
515
|
+
// ❌ Testing multiple things
|
|
516
|
+
test('user service', () => {
|
|
517
|
+
expect(createUser()).toBeDefined();
|
|
518
|
+
expect(updateUser()).toBeTruthy();
|
|
519
|
+
expect(deleteUser()).toBeNull();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// ✅ One test per behavior
|
|
523
|
+
test('should create user with valid data', () => {
|
|
524
|
+
expect(createUser(validData)).toBeDefined();
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test('should update existing user', () => {
|
|
528
|
+
expect(updateUser(user, newData)).toBeTruthy();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test('should delete user and return null', () => {
|
|
532
|
+
expect(deleteUser(userId)).toBeNull();
|
|
533
|
+
});
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### TDD Discipline
|
|
537
|
+
|
|
538
|
+
#### Write Test First
|
|
539
|
+
```
|
|
540
|
+
❌ Wrong order:
|
|
541
|
+
1. Write code
|
|
542
|
+
2. Write test
|
|
543
|
+
3. Run test
|
|
544
|
+
|
|
545
|
+
✅ TDD order:
|
|
546
|
+
1. Write test (RED)
|
|
547
|
+
2. Write code (GREEN)
|
|
548
|
+
3. Refactor (GREEN)
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
#### Smallest Steps
|
|
552
|
+
```
|
|
553
|
+
✅ Start with simplest test:
|
|
554
|
+
test('should return empty array when no input', () => {
|
|
555
|
+
expect(process([])).toEqual([]);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
Then build up:
|
|
559
|
+
test('should process single item', () => {
|
|
560
|
+
expect(process([1])).toEqual([2]);
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test('should process multiple items', () => {
|
|
564
|
+
expect(process([1, 2, 3])).toEqual([2, 4, 6]);
|
|
565
|
+
});
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
#### Run Tests Frequently
|
|
569
|
+
```bash
|
|
570
|
+
# After writing test (should fail)
|
|
571
|
+
bun test
|
|
572
|
+
|
|
573
|
+
# After writing code (should pass)
|
|
574
|
+
bun test
|
|
575
|
+
|
|
576
|
+
# After refactoring (should still pass)
|
|
577
|
+
bun test
|
|
578
|
+
|
|
579
|
+
# Run tests constantly!
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Common TDD Mistakes
|
|
583
|
+
|
|
584
|
+
#### ❌ Writing Multiple Tests Before Code
|
|
585
|
+
```typescript
|
|
586
|
+
// Don't do this:
|
|
587
|
+
describe('Calculator', () => {
|
|
588
|
+
test('should add', () => {});
|
|
589
|
+
test('should subtract', () => {});
|
|
590
|
+
test('should multiply', () => {});
|
|
591
|
+
test('should divide', () => {});
|
|
592
|
+
});
|
|
593
|
+
// Then implement all at once
|
|
594
|
+
|
|
595
|
+
// Do this instead: One test at a time
|
|
596
|
+
// Write test for add → implement add → refactor
|
|
597
|
+
// Write test for subtract → implement subtract → refactor
|
|
598
|
+
// ...
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
#### ❌ Skipping RED Phase
|
|
602
|
+
```
|
|
603
|
+
Don't assume test will fail - VERIFY IT!
|
|
604
|
+
|
|
605
|
+
1. Write test
|
|
606
|
+
2. Run test → see it FAIL
|
|
607
|
+
3. Then write code
|
|
608
|
+
|
|
609
|
+
If test passes without implementation:
|
|
610
|
+
- Test might be wrong
|
|
611
|
+
- Feature might already exist
|
|
612
|
+
- False positive
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
#### ❌ Over-implementing in GREEN Phase
|
|
616
|
+
```typescript
|
|
617
|
+
// ❌ Test only checks addition, but you implement:
|
|
618
|
+
function calculate(a, b, operation) {
|
|
619
|
+
switch(operation) {
|
|
620
|
+
case 'add': return a + b;
|
|
621
|
+
case 'subtract': return a - b; // Not tested!
|
|
622
|
+
case 'multiply': return a * b; // Not tested!
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ✅ Only implement what test needs:
|
|
627
|
+
function calculate(a, b) {
|
|
628
|
+
return a + b; // Just this
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
#### ❌ Refactoring Without Running Tests
|
|
633
|
+
```
|
|
634
|
+
Every refactor MUST be followed by test run:
|
|
635
|
+
|
|
636
|
+
1. Extract method → run tests
|
|
637
|
+
2. Rename variable → run tests
|
|
638
|
+
3. Simplify logic → run tests
|
|
639
|
+
|
|
640
|
+
Never skip this step!
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
## Quick Reference
|
|
646
|
+
|
|
647
|
+
### Architecture Docs
|
|
648
|
+
| Stack | Doc |
|
|
649
|
+
|-------|-----|
|
|
650
|
+
| All | `clean-architecture.md` |
|
|
651
|
+
| Flutter | `flutter-mobile.md` |
|
|
652
|
+
| React | `react-frontend.md` |
|
|
653
|
+
| Go | `go-backend.md` |
|
|
654
|
+
| Laravel | `laravel-backend.md` |
|
|
655
|
+
| Remix | `remix-fullstack.md` |
|
|
656
|
+
| Monorepo | `monorepo.md` |
|
|
657
|
+
|
|
658
|
+
### TDD Cycle Summary
|
|
659
|
+
| Phase | Key Actions | Gate |
|
|
660
|
+
|-------|-------------|------|
|
|
661
|
+
| **RED** | Write failing test | Test fails (right reason) |
|
|
662
|
+
| **GREEN** | Minimal code to pass | Test passes |
|
|
663
|
+
| **REFACTOR** | Clean code | Tests still pass |
|
|
664
|
+
|
|
665
|
+
### Test Commands by Stack
|
|
666
|
+
```bash
|
|
667
|
+
# Flutter
|
|
668
|
+
flutter test
|
|
669
|
+
flutter test --coverage
|
|
670
|
+
|
|
671
|
+
# Go
|
|
672
|
+
go test ./...
|
|
673
|
+
go test -v ./...
|
|
674
|
+
go test -cover ./...
|
|
675
|
+
|
|
676
|
+
# React/Remix
|
|
677
|
+
bun test
|
|
678
|
+
bun test --coverage
|
|
679
|
+
bun test --watch
|
|
680
|
+
|
|
681
|
+
# Laravel
|
|
682
|
+
php artisan test
|
|
683
|
+
php artisan test --coverage
|
|
684
|
+
php artisan test --parallel
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Test File Locations
|
|
688
|
+
| Stack | Location Pattern |
|
|
689
|
+
|-------|------------------|
|
|
690
|
+
| Flutter | `test/[feature]_test.dart` |
|
|
691
|
+
| React | `src/[feature].test.ts` |
|
|
692
|
+
| Go | `[package]_test.go` (same dir) |
|
|
693
|
+
| Laravel | `tests/Unit/[Feature]Test.php` |
|
|
694
|
+
| Remix | `app/[feature].test.ts` |
|
|
695
|
+
|
|
696
|
+
### TDD Mantras
|
|
697
|
+
|
|
698
|
+
```
|
|
699
|
+
🔴 RED: "Write a test that fails"
|
|
700
|
+
🟢 GREEN: "Make it work"
|
|
701
|
+
🔵 REFACTOR: "Make it right"
|
|
702
|
+
|
|
703
|
+
"Test first, code second"
|
|
704
|
+
"One test, one behavior"
|
|
705
|
+
"Minimal code, maximum coverage"
|
|
706
|
+
"Refactor fearlessly, tests protect you"
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### When to Loop Back
|
|
710
|
+
|
|
711
|
+
- After REFACTOR → Return to RED for next behavior
|
|
712
|
+
- Test won't fail → Fix the test (RED)
|
|
713
|
+
- Test won't pass → Fix the code (GREEN)
|
|
714
|
+
- Code is messy → Refactor (REFACTOR)
|
|
715
|
+
|
|
716
|
+
### Success Criteria
|
|
717
|
+
|
|
718
|
+
TDD session complete when:
|
|
719
|
+
1. All behaviors have tests (RED → GREEN)
|
|
720
|
+
2. Code is clean (REFACTOR)
|
|
721
|
+
3. All tests pass
|
|
722
|
+
4. Architecture compliance verified
|
|
723
|
+
5. No TODO/hardcoded values left
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## TDD Workflow Example
|
|
728
|
+
|
|
729
|
+
### Example: Implementing a Shopping Cart
|
|
730
|
+
|
|
731
|
+
#### Cycle 1: Add item
|
|
732
|
+
|
|
733
|
+
**RED**
|
|
734
|
+
```typescript
|
|
735
|
+
test('should add item to empty cart', () => {
|
|
736
|
+
const cart = new ShoppingCart();
|
|
737
|
+
cart.addItem({ id: 1, name: 'Book', price: 10 });
|
|
738
|
+
expect(cart.items).toHaveLength(1);
|
|
739
|
+
});
|
|
740
|
+
// Run: ❌ FAIL - ShoppingCart not defined
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
**GREEN**
|
|
744
|
+
```typescript
|
|
745
|
+
class ShoppingCart {
|
|
746
|
+
items: Item[] = [];
|
|
747
|
+
addItem(item: Item) {
|
|
748
|
+
this.items.push(item);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
// Run: ✅ PASS
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
**REFACTOR**
|
|
755
|
+
```typescript
|
|
756
|
+
// Code is clean, nothing to refactor
|
|
757
|
+
// Run: ✅ PASS
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
#### Cycle 2: Calculate total
|
|
761
|
+
|
|
762
|
+
**RED**
|
|
763
|
+
```typescript
|
|
764
|
+
test('should calculate total of items', () => {
|
|
765
|
+
const cart = new ShoppingCart();
|
|
766
|
+
cart.addItem({ id: 1, name: 'Book', price: 10 });
|
|
767
|
+
cart.addItem({ id: 2, name: 'Pen', price: 5 });
|
|
768
|
+
expect(cart.getTotal()).toBe(15);
|
|
769
|
+
});
|
|
770
|
+
// Run: ❌ FAIL - getTotal is not a function
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**GREEN**
|
|
774
|
+
```typescript
|
|
775
|
+
class ShoppingCart {
|
|
776
|
+
items: Item[] = [];
|
|
777
|
+
|
|
778
|
+
addItem(item: Item) {
|
|
779
|
+
this.items.push(item);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
getTotal() {
|
|
783
|
+
return this.items.reduce((sum, item) => sum + item.price, 0);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
// Run: ✅ PASS
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
**REFACTOR**
|
|
790
|
+
```typescript
|
|
791
|
+
// Extract calculation logic
|
|
792
|
+
class ShoppingCart {
|
|
793
|
+
items: Item[] = [];
|
|
794
|
+
|
|
795
|
+
addItem(item: Item) {
|
|
796
|
+
this.items.push(item);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
getTotal() {
|
|
800
|
+
return this.calculateTotal(this.items);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
private calculateTotal(items: Item[]): number {
|
|
804
|
+
return items.reduce((sum, item) => sum + item.price, 0);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// Run: ✅ PASS
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
#### Cycle 3: Continue for each behavior...
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
814
|
+
## Resources
|
|
815
|
+
|
|
816
|
+
### Learn More
|
|
817
|
+
- Kent Beck's "Test Driven Development: By Example"
|
|
818
|
+
- Martin Fowler's "Refactoring"
|
|
819
|
+
- Uncle Bob's TDD tutorials
|
|
820
|
+
|
|
821
|
+
### Test Frameworks
|
|
822
|
+
| Stack | Framework |
|
|
823
|
+
|-------|-----------|
|
|
824
|
+
| Flutter | flutter_test, mockito |
|
|
825
|
+
| React | Vitest, Jest, Testing Library |
|
|
826
|
+
| Go | testing (stdlib), testify |
|
|
827
|
+
| Laravel | PHPUnit, Pest |
|
|
828
|
+
| Remix | Vitest, Playwright |
|