agentic-team-templates 0.4.2 → 0.6.0

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.
@@ -0,0 +1,294 @@
1
+ # TDD Methodology
2
+
3
+ Guidelines for practicing Test-Driven Development effectively.
4
+
5
+ ## The Red-Green-Refactor Cycle
6
+
7
+ TDD follows a disciplined three-step cycle:
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────┐
11
+ │ │
12
+ │ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
13
+ │ │ RED │ ───► │ GREEN │ ───► │ REFACTOR │ ───┐ │
14
+ │ │ │ │ │ │ │ │ │
15
+ │ │ Write │ │ Make │ │ Improve │ │ │
16
+ │ │ failing │ │ it │ │ code │ │ │
17
+ │ │ test │ │ pass │ │ quality │ │ │
18
+ │ └─────────┘ └─────────┘ └─────────────┘ │ │
19
+ │ ▲ │ │
20
+ │ └────────────────────────────────────────────────┘ │
21
+ │ │
22
+ └─────────────────────────────────────────────────────────────┘
23
+ ```
24
+
25
+ ### Step 1: RED (Write Failing Test)
26
+
27
+ Write a test that describes the behavior you want. It should fail because the code doesn't exist yet.
28
+
29
+ ```ts
30
+ describe('calculateTax', () => {
31
+ it('applies 10% tax rate to standard items', () => {
32
+ const result = calculateTax(100, 'standard');
33
+ expect(result).toBe(110);
34
+ });
35
+ });
36
+ ```
37
+
38
+ Run the test. Watch it fail. **This confirms your test is actually testing something.**
39
+
40
+ ### Step 2: GREEN (Make It Pass)
41
+
42
+ Write the **minimum** code necessary to make the test pass. Don't over-engineer.
43
+
44
+ ```ts
45
+ function calculateTax(amount: number, type: string): number {
46
+ if (type === 'standard') {
47
+ return amount * 1.10;
48
+ }
49
+ return amount;
50
+ }
51
+ ```
52
+
53
+ ### Step 3: REFACTOR (Improve Quality)
54
+
55
+ Now that tests pass, improve the code without changing behavior. Tests give you confidence to refactor.
56
+
57
+ ```ts
58
+ const TAX_RATES: Record<string, number> = {
59
+ standard: 0.10,
60
+ reduced: 0.05,
61
+ exempt: 0,
62
+ };
63
+
64
+ function calculateTax(amount: number, type: TaxType): number {
65
+ const rate = TAX_RATES[type] ?? 0;
66
+ return amount * (1 + rate);
67
+ }
68
+ ```
69
+
70
+ ## TDD Best Practices
71
+
72
+ ### Keep Cycles Short
73
+
74
+ Each red-green-refactor cycle should take **2-10 minutes**. If you're stuck in red for more than 10 minutes, your slice is too big.
75
+
76
+ ### One Test at a Time
77
+
78
+ Write one failing test, make it pass, refactor. Don't write multiple failing tests.
79
+
80
+ ### Start with the Simplest Case
81
+
82
+ ```ts
83
+ // Start here
84
+ it('returns 0 for empty cart', () => {
85
+ expect(calculateTotal([])).toBe(0);
86
+ });
87
+
88
+ // Then add complexity
89
+ it('sums single item price', () => {
90
+ expect(calculateTotal([{ price: 100 }])).toBe(100);
91
+ });
92
+
93
+ // Then more
94
+ it('sums multiple items', () => {
95
+ expect(calculateTotal([{ price: 100 }, { price: 50 }])).toBe(150);
96
+ });
97
+ ```
98
+
99
+ ### Test the API, Not the Implementation
100
+
101
+ Write tests against the public interface. Don't test private methods.
102
+
103
+ ```ts
104
+ // Bad: Testing internals
105
+ it('calls _parseInput internally', () => {
106
+ const spy = vi.spyOn(parser, '_parseInput');
107
+ parser.parse('data');
108
+ expect(spy).toHaveBeenCalled();
109
+ });
110
+
111
+ // Good: Testing behavior
112
+ it('parses valid JSON', () => {
113
+ const result = parser.parse('{"key": "value"}');
114
+ expect(result).toEqual({ key: 'value' });
115
+ });
116
+ ```
117
+
118
+ ### Write the Test You Wish You Had
119
+
120
+ Ask yourself: "What test would give me confidence this works?" Then write that test.
121
+
122
+ ## TDD Patterns
123
+
124
+ ### Triangulation
125
+
126
+ Use multiple examples to drive toward a general solution:
127
+
128
+ ```ts
129
+ it('calculates shipping for weight 1kg', () => {
130
+ expect(calculateShipping(1)).toBe(5);
131
+ });
132
+
133
+ it('calculates shipping for weight 2kg', () => {
134
+ expect(calculateShipping(2)).toBe(10);
135
+ });
136
+
137
+ // Now the pattern is clear: $5/kg
138
+ ```
139
+
140
+ ### Transformation Priority Premise
141
+
142
+ When making tests pass, prefer simpler transformations:
143
+
144
+ 1. `{} → nil` (empty to null/nil)
145
+ 2. `nil → constant` (null to specific value)
146
+ 3. `constant → constant+` (simple to more complex)
147
+ 4. `constant → scalar` (to variable)
148
+ 5. `statement → statements` (add more statements)
149
+ 6. `unconditional → if` (add conditional)
150
+ 7. `scalar → collection` (to array/list)
151
+ 8. `collection → collection+` (add to collection)
152
+ 9. `statement → recursion` (add recursion)
153
+ 10. `if → while` (conditional to loop)
154
+ 11. `expression → function` (extract expression)
155
+ 12. `variable → assignment` (complex assignment)
156
+
157
+ ### Test List
158
+
159
+ Before coding, write a list of tests you think you'll need:
160
+
161
+ ```
162
+ calculateDiscount:
163
+ - [ ] returns 0 for no discount
164
+ - [ ] applies percentage discount
165
+ - [ ] applies fixed discount
166
+ - [ ] caps discount at item price (no negative)
167
+ - [ ] handles zero price
168
+ - [ ] throws for negative discount value
169
+ ```
170
+
171
+ Work through the list, adding tests you discover along the way.
172
+
173
+ ## When TDD Adds Maximum Value
174
+
175
+ - **Complex business logic** - Rules that are easy to get wrong
176
+ - **Algorithms** - Sort, search, validation
177
+ - **State machines** - Transitions, edge cases
178
+ - **Financial calculations** - Money, taxes, discounts
179
+ - **Security logic** - Auth, permissions, validation
180
+ - **Integration points** - APIs, databases, queues
181
+
182
+ ## When to Adapt TDD
183
+
184
+ ### Spike and Stabilize
185
+
186
+ For exploratory work, spike first, then write tests:
187
+
188
+ 1. Write throwaway code to understand the problem
189
+ 2. Delete the spike
190
+ 3. TDD the real implementation
191
+
192
+ ### Outside-In vs Inside-Out
193
+
194
+ **Outside-In (London School):**
195
+ - Start with E2E test
196
+ - Mock dependencies
197
+ - Work inward
198
+
199
+ **Inside-Out (Chicago School):**
200
+ - Start with domain logic
201
+ - Build up from units
202
+ - Integrate at the end
203
+
204
+ Choose based on what you're building.
205
+
206
+ ## Common TDD Mistakes
207
+
208
+ ### 1. Skipping the Red Phase
209
+
210
+ If you write code before the test, you don't know if the test works.
211
+
212
+ ### 2. Writing Too Many Tests at Once
213
+
214
+ This defeats the purpose. Write one test, make it pass, repeat.
215
+
216
+ ### 3. Not Refactoring
217
+
218
+ The refactor step is not optional. Skipping it leaves messy code.
219
+
220
+ ### 4. Testing Implementation Details
221
+
222
+ This makes refactoring painful. Test behavior, not internals.
223
+
224
+ ### 5. Giant Leaps
225
+
226
+ If your test requires lots of code to pass, break it into smaller tests.
227
+
228
+ ## Example: TDD Session
229
+
230
+ Let's TDD a password validator:
231
+
232
+ ```ts
233
+ // Test 1: Red
234
+ it('rejects empty password', () => {
235
+ expect(validatePassword('')).toEqual({ valid: false, errors: ['Password is required'] });
236
+ });
237
+
238
+ // Green
239
+ function validatePassword(password: string) {
240
+ if (!password) {
241
+ return { valid: false, errors: ['Password is required'] };
242
+ }
243
+ return { valid: true, errors: [] };
244
+ }
245
+
246
+ // Test 2: Red
247
+ it('rejects password under 8 characters', () => {
248
+ expect(validatePassword('abc')).toEqual({
249
+ valid: false,
250
+ errors: ['Password must be at least 8 characters']
251
+ });
252
+ });
253
+
254
+ // Green
255
+ function validatePassword(password: string) {
256
+ const errors: string[] = [];
257
+
258
+ if (!password) {
259
+ errors.push('Password is required');
260
+ } else if (password.length < 8) {
261
+ errors.push('Password must be at least 8 characters');
262
+ }
263
+
264
+ return { valid: errors.length === 0, errors };
265
+ }
266
+
267
+ // Test 3: Red
268
+ it('requires at least one uppercase letter', () => {
269
+ expect(validatePassword('abcdefgh')).toEqual({
270
+ valid: false,
271
+ errors: ['Password must contain at least one uppercase letter'],
272
+ });
273
+ });
274
+
275
+ // Green + Refactor
276
+ const RULES = [
277
+ { test: (p: string) => p.length >= 8, error: 'Password must be at least 8 characters' },
278
+ { test: (p: string) => /[A-Z]/.test(p), error: 'Password must contain at least one uppercase letter' },
279
+ ];
280
+
281
+ function validatePassword(password: string) {
282
+ if (!password) {
283
+ return { valid: false, errors: ['Password is required'] };
284
+ }
285
+
286
+ const errors = RULES
287
+ .filter(rule => !rule.test(password))
288
+ .map(rule => rule.error);
289
+
290
+ return { valid: errors.length === 0, errors };
291
+ }
292
+ ```
293
+
294
+ Each cycle is small, focused, and builds confidence.