omgkit 2.2.0 → 2.3.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.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  3. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  4. package/plugin/skills/databases/redis/SKILL.md +53 -860
  5. package/plugin/skills/devops/aws/SKILL.md +68 -672
  6. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  7. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  8. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  9. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  10. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  12. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  14. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  15. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  16. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  17. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  18. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  19. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  20. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  21. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  22. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  23. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  24. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  25. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  26. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  27. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  28. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  29. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  30. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  31. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  32. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  33. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  34. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  35. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  36. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  37. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  38. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  39. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  40. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  41. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  42. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  43. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  44. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  45. package/plugin/skills/security/oauth/SKILL.md +80 -934
  46. package/plugin/skills/security/owasp/SKILL.md +78 -862
  47. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  48. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  49. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  50. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  51. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  52. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  53. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  54. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  55. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,794 +1,133 @@
1
1
  ---
2
- name: test-driven-development
3
- description: Test-first development with Red-Green-Refactor cycle, behavior-driven specs, and comprehensive testing strategies
4
- category: methodology
5
- triggers:
6
- - tdd
7
- - test driven
8
- - test first
9
- - red green refactor
10
- - write tests first
11
- - testing methodology
2
+ name: developing-test-driven
3
+ description: AI agent practices test-first development with the Red-Green-Refactor cycle for confident, well-designed code. Use when implementing features, fixing bugs, or establishing testing practices.
12
4
  ---
13
5
 
14
- # Test-Driven Development
6
+ # Developing Test-Driven
15
7
 
16
- Master **test-first development** with the Red-Green-Refactor cycle. This skill enables confident code changes through comprehensive test coverage, emergent design, and continuous validation.
8
+ ## Quick Start
17
9
 
18
- ## Purpose
19
-
20
- Build reliable software through test-first practices:
21
-
22
- - Write failing tests before implementation
23
- - Design interfaces through test usage
24
- - Achieve comprehensive coverage naturally
25
- - Enable fearless refactoring
26
- - Document behavior through specs
27
- - Catch regressions immediately
28
- - Improve code design through testability
10
+ 1. **Red** - Write a failing test that defines desired behavior
11
+ 2. **Green** - Write minimal code to make the test pass
12
+ 3. **Refactor** - Improve code while keeping tests green
13
+ 4. **Repeat** - Continue with next behavior (1-5 minute cycles)
29
14
 
30
15
  ## Features
31
16
 
32
- ### 1. The Red-Green-Refactor Cycle
33
-
34
- ```markdown
35
- ## TDD Cycle Visualization
36
-
37
- ┌───────────────────────────────────────────────────┐
38
- │ │
39
- │ 🔴 RED │
40
- │ Write a failing test │
41
- │ - Test doesn't compile OR │
42
- │ - Test fails with assertion error │
43
- │ │
44
- │ │ │
45
- │ ▼ │
46
- │ │
47
- │ 🟢 GREEN │
48
- │ Make the test pass │
49
- │ - Write MINIMAL code │
50
- │ - No more than needed to pass │
51
- │ - "Fake it till you make it" │
52
- │ │
53
- │ │ │
54
- │ ▼ │
55
- │ │
56
- │ 🔄 REFACTOR │
57
- │ Improve the code │
58
- │ - Remove duplication │
59
- │ - Improve naming │
60
- │ - Extract methods/classes │
61
- │ - Keep tests green! │
62
- │ │
63
- │ │ │
64
- │ ▼ │
65
- │ │
66
- │ Next Test ──────────────────► 🔴 │
67
- │ │
68
- └───────────────────────────────────────────────────┘
69
-
70
- ## Cycle Duration
71
- - Ideal cycle: 1-5 minutes
72
- - Maximum cycle: 10 minutes
73
- - If stuck longer: Undo and try smaller step
74
- ```
17
+ | Feature | Description | Guide |
18
+ |---------|-------------|-------|
19
+ | Red-Green-Refactor | Core TDD cycle | Fail -> Pass -> Improve -> Repeat |
20
+ | Test Patterns | Effective test structures | Arrange-Act-Assert, Given-When-Then |
21
+ | Test Fixtures | Reusable test setup | beforeEach, factories, builders |
22
+ | Mocking Strategies | Isolate dependencies | Inject deps, mock boundaries only |
23
+ | Parameterized Tests | Test multiple inputs | `it.each` for input variations |
24
+ | Coverage Analysis | Verify thoroughness | Statements, branches, functions |
75
25
 
76
- ### 2. Writing Effective Tests
26
+ ## Common Patterns
77
27
 
78
28
  ```typescript
79
- // TDD Example: Building a Password Validator
80
-
81
- // ═══════════════════════════════════════════════════════════
82
- // ITERATION 1: Minimum length requirement
83
- // ═══════════════════════════════════════════════════════════
84
-
85
- // 🔴 RED: Write failing test first
86
- describe('PasswordValidator', () => {
87
- describe('validate', () => {
88
- it('returns invalid for passwords shorter than 8 characters', () => {
89
- const result = validatePassword('short');
90
- expect(result.valid).toBe(false);
91
- expect(result.errors).toContain('Password must be at least 8 characters');
92
- });
93
- });
94
- });
95
-
96
- // 🟢 GREEN: Minimal implementation to pass
97
- interface ValidationResult {
98
- valid: boolean;
99
- errors: string[];
100
- }
101
-
102
- function validatePassword(password: string): ValidationResult {
103
- const errors: string[] = [];
104
-
105
- if (password.length < 8) {
106
- errors.push('Password must be at least 8 characters');
107
- }
108
-
109
- return {
110
- valid: errors.length === 0,
111
- errors,
112
- };
113
- }
114
-
115
- // 🔄 REFACTOR: Nothing to refactor yet
116
-
117
- // ═══════════════════════════════════════════════════════════
118
- // ITERATION 2: Valid password passes
119
- // ═══════════════════════════════════════════════════════════
120
-
121
- // 🔴 RED: Add test for valid case
122
- it('returns valid for password with 8+ characters', () => {
123
- const result = validatePassword('validpwd');
124
- expect(result.valid).toBe(true);
125
- expect(result.errors).toHaveLength(0);
126
- });
127
-
128
- // 🟢 GREEN: Already passes! No changes needed.
29
+ // RED-GREEN-REFACTOR CYCLE
129
30
 
130
- // ═══════════════════════════════════════════════════════════
131
- // ITERATION 3: Require uppercase letter
132
- // ═══════════════════════════════════════════════════════════
133
-
134
- // 🔴 RED: Add uppercase requirement
135
- it('returns invalid without uppercase letter', () => {
136
- const result = validatePassword('lowercase123');
31
+ // RED: Write failing test
32
+ it('rejects passwords shorter than 8 characters', () => {
33
+ const result = validatePassword('short');
137
34
  expect(result.valid).toBe(false);
138
- expect(result.errors).toContain('Password must contain uppercase letter');
35
+ expect(result.errors).toContain('Password must be at least 8 characters');
139
36
  });
140
37
 
141
- // 🟢 GREEN: Add uppercase check
142
- function validatePassword(password: string): ValidationResult {
143
- const errors: string[] = [];
144
-
38
+ // GREEN: Minimal implementation
39
+ function validatePassword(password: string) {
40
+ const errors = [];
145
41
  if (password.length < 8) {
146
42
  errors.push('Password must be at least 8 characters');
147
43
  }
148
-
149
- if (!/[A-Z]/.test(password)) {
150
- errors.push('Password must contain uppercase letter');
151
- }
152
-
153
- return {
154
- valid: errors.length === 0,
155
- errors,
156
- };
157
- }
158
-
159
- // 🔄 REFACTOR: Extract validation rules
160
- interface ValidationRule {
161
- test: (password: string) => boolean;
162
- message: string;
44
+ return { valid: errors.length === 0, errors };
163
45
  }
164
46
 
165
- const passwordRules: ValidationRule[] = [
166
- {
167
- test: (p) => p.length >= 8,
168
- message: 'Password must be at least 8 characters',
169
- },
170
- {
171
- test: (p) => /[A-Z]/.test(p),
172
- message: 'Password must contain uppercase letter',
173
- },
47
+ // REFACTOR: Extract rules (tests stay green)
48
+ const rules = [
49
+ { test: (p) => p.length >= 8, message: 'Must be 8+ chars' },
50
+ { test: (p) => /[A-Z]/.test(p), message: 'Must have uppercase' },
174
51
  ];
175
-
176
- function validatePassword(password: string): ValidationResult {
177
- const errors = passwordRules
178
- .filter((rule) => !rule.test(password))
179
- .map((rule) => rule.message);
180
-
181
- return {
182
- valid: errors.length === 0,
183
- errors,
184
- };
52
+ function validatePassword(password: string) {
53
+ const errors = rules.filter(r => !r.test(password)).map(r => r.message);
54
+ return { valid: errors.length === 0, errors };
185
55
  }
186
-
187
- // ═══════════════════════════════════════════════════════════
188
- // ITERATION 4: More rules become easy to add!
189
- // ═══════════════════════════════════════════════════════════
190
-
191
- // 🔴 RED
192
- it('returns invalid without number', () => {
193
- const result = validatePassword('NoNumbers!');
194
- expect(result.valid).toBe(false);
195
- expect(result.errors).toContain('Password must contain a number');
196
- });
197
-
198
- // 🟢 GREEN: Just add to rules array
199
- passwordRules.push({
200
- test: (p) => /\d/.test(p),
201
- message: 'Password must contain a number',
202
- });
203
56
  ```
204
57
 
205
- ### 3. Test Patterns and Structures
206
-
207
58
  ```typescript
208
- // ═══════════════════════════════════════════════════════════
209
59
  // ARRANGE-ACT-ASSERT Pattern
210
- // ═══════════════════════════════════════════════════════════
211
-
212
- describe('ShoppingCart', () => {
213
- describe('addItem', () => {
214
- it('increases total when item is added', () => {
215
- // ARRANGE: Set up test data and dependencies
216
- const cart = new ShoppingCart();
217
- const item = { id: '1', name: 'Widget', price: 10.00 };
218
-
219
- // ACT: Execute the behavior being tested
220
- cart.addItem(item);
221
-
222
- // ASSERT: Verify the expected outcome
223
- expect(cart.total).toBe(10.00);
224
- expect(cart.itemCount).toBe(1);
225
- });
226
- });
227
- });
228
-
229
- // ═══════════════════════════════════════════════════════════
230
- // GIVEN-WHEN-THEN Pattern (BDD Style)
231
- // ═══════════════════════════════════════════════════════════
232
-
233
- describe('ShoppingCart', () => {
234
- describe('when applying discount code', () => {
235
- it('reduces total by discount percentage', () => {
236
- // GIVEN a cart with items
237
- const cart = new ShoppingCart();
238
- cart.addItem({ id: '1', name: 'Widget', price: 100.00 });
239
-
240
- // WHEN a 20% discount is applied
241
- cart.applyDiscount('SAVE20');
242
-
243
- // THEN the total is reduced by 20%
244
- expect(cart.total).toBe(80.00);
245
- });
246
- });
247
- });
248
-
249
- // ═══════════════════════════════════════════════════════════
250
- // Test Fixture Pattern
251
- // ═══════════════════════════════════════════════════════════
252
-
253
- describe('OrderService', () => {
254
- // Shared fixtures
255
- let orderService: OrderService;
256
- let mockPaymentGateway: jest.Mocked<PaymentGateway>;
257
- let mockInventory: jest.Mocked<InventoryService>;
258
-
259
- // Common test data
260
- const validOrder: Order = {
261
- id: 'order-123',
262
- items: [{ productId: 'prod-1', quantity: 2, price: 25.00 }],
263
- total: 50.00,
264
- };
265
-
266
- beforeEach(() => {
267
- // Fresh mocks for each test
268
- mockPaymentGateway = {
269
- charge: jest.fn().mockResolvedValue({ success: true }),
270
- };
271
- mockInventory = {
272
- reserve: jest.fn().mockResolvedValue(true),
273
- release: jest.fn().mockResolvedValue(true),
274
- };
275
-
276
- orderService = new OrderService(mockPaymentGateway, mockInventory);
277
- });
278
-
279
- afterEach(() => {
280
- jest.clearAllMocks();
281
- });
282
-
283
- describe('submitOrder', () => {
284
- it('reserves inventory and charges payment', async () => {
285
- await orderService.submitOrder(validOrder);
286
-
287
- expect(mockInventory.reserve).toHaveBeenCalledWith(validOrder.items);
288
- expect(mockPaymentGateway.charge).toHaveBeenCalledWith(50.00);
289
- });
290
-
291
- it('releases inventory if payment fails', async () => {
292
- mockPaymentGateway.charge.mockResolvedValue({ success: false });
293
-
294
- await expect(orderService.submitOrder(validOrder)).rejects.toThrow();
295
-
296
- expect(mockInventory.release).toHaveBeenCalledWith(validOrder.items);
297
- });
298
- });
299
- });
300
-
301
- // ═══════════════════════════════════════════════════════════
302
- // Parameterized Tests
303
- // ═══════════════════════════════════════════════════════════
304
-
305
- describe('EmailValidator', () => {
306
- // Valid email test cases
307
- it.each([
308
- ['simple@example.com', 'simple email'],
309
- ['user.name@domain.org', 'email with dots'],
310
- ['user+tag@example.com', 'email with plus'],
311
- ['user@subdomain.domain.com', 'email with subdomain'],
312
- ])('validates %s as valid (%s)', (email, _description) => {
313
- expect(isValidEmail(email)).toBe(true);
314
- });
315
-
316
- // Invalid email test cases
317
- it.each([
318
- ['plaintext', 'no @ symbol'],
319
- ['missing@domain', 'no TLD'],
320
- ['@nodomain.com', 'no local part'],
321
- ['spaces in@email.com', 'contains spaces'],
322
- ['', 'empty string'],
323
- ])('rejects %s as invalid (%s)', (email, _description) => {
324
- expect(isValidEmail(email)).toBe(false);
325
- });
326
- });
327
-
328
- // ═══════════════════════════════════════════════════════════
329
- // Builder Pattern for Test Data
330
- // ═══════════════════════════════════════════════════════════
331
-
332
- class UserBuilder {
333
- private user: Partial<User> = {
334
- id: 'user-123',
335
- email: 'test@example.com',
336
- name: 'Test User',
337
- role: 'user',
338
- active: true,
339
- };
340
-
341
- withId(id: string): this {
342
- this.user.id = id;
343
- return this;
344
- }
345
-
346
- withEmail(email: string): this {
347
- this.user.email = email;
348
- return this;
349
- }
350
-
351
- withRole(role: UserRole): this {
352
- this.user.role = role;
353
- return this;
354
- }
355
-
356
- inactive(): this {
357
- this.user.active = false;
358
- return this;
359
- }
360
-
361
- admin(): this {
362
- this.user.role = 'admin';
363
- return this;
364
- }
365
-
366
- build(): User {
367
- return this.user as User;
368
- }
369
- }
370
-
371
- // Usage in tests
372
- describe('PermissionService', () => {
373
- it('grants admin access to admin users', () => {
374
- const admin = new UserBuilder().admin().build();
375
- const service = new PermissionService();
376
-
377
- expect(service.canAccessAdminPanel(admin)).toBe(true);
378
- });
379
-
380
- it('denies admin access to regular users', () => {
381
- const user = new UserBuilder().withRole('user').build();
382
- const service = new PermissionService();
383
-
384
- expect(service.canAccessAdminPanel(user)).toBe(false);
385
- });
386
- });
387
- ```
388
-
389
- ### 4. Mocking Strategies
390
-
391
- ```typescript
392
- // ═══════════════════════════════════════════════════════════
393
- // Dependency Injection for Testability
394
- // ═══════════════════════════════════════════════════════════
395
-
396
- // BAD: Hard to test
397
- class UserService {
398
- async getUser(id: string) {
399
- const response = await fetch(`/api/users/${id}`); // Can't mock
400
- return response.json();
401
- }
402
- }
60
+ it('increases total when item added', () => {
61
+ // ARRANGE
62
+ const cart = new ShoppingCart();
63
+ const item = { id: '1', price: 10.00 };
403
64
 
404
- // GOOD: Dependency injection
405
- interface HttpClient {
406
- get<T>(url: string): Promise<T>;
407
- }
408
-
409
- class UserService {
410
- constructor(private http: HttpClient) {}
411
-
412
- async getUser(id: string): Promise<User> {
413
- return this.http.get<User>(`/api/users/${id}`);
414
- }
415
- }
416
-
417
- // Test with mock
418
- describe('UserService', () => {
419
- it('fetches user by id', async () => {
420
- const mockHttp: jest.Mocked<HttpClient> = {
421
- get: jest.fn().mockResolvedValue({ id: '1', name: 'John' }),
422
- };
65
+ // ACT
66
+ cart.addItem(item);
423
67
 
424
- const service = new UserService(mockHttp);
425
- const user = await service.getUser('1');
426
-
427
- expect(mockHttp.get).toHaveBeenCalledWith('/api/users/1');
428
- expect(user.name).toBe('John');
429
- });
68
+ // ASSERT
69
+ expect(cart.total).toBe(10.00);
430
70
  });
431
71
 
432
- // ═══════════════════════════════════════════════════════════
433
- // Stub vs Mock vs Spy
434
- // ═══════════════════════════════════════════════════════════
435
-
436
- // STUB: Provides canned responses
437
- const stubLogger: Logger = {
438
- log: () => {},
439
- error: () => {},
440
- warn: () => {},
441
- };
442
-
443
- // MOCK: Verifies interactions
444
- const mockLogger = {
445
- log: jest.fn(),
446
- error: jest.fn(),
447
- warn: jest.fn(),
448
- };
449
-
450
- // SPY: Wraps real implementation
451
- const realLogger = new ConsoleLogger();
452
- const spyLogger = jest.spyOn(realLogger, 'error');
453
-
454
- // ═══════════════════════════════════════════════════════════
455
- // Time-Based Testing
456
- // ═══════════════════════════════════════════════════════════
457
-
458
- describe('TokenService', () => {
459
- beforeEach(() => {
460
- jest.useFakeTimers();
461
- });
462
-
463
- afterEach(() => {
464
- jest.useRealTimers();
465
- });
466
-
467
- it('expires tokens after 1 hour', () => {
468
- const service = new TokenService();
469
- const token = service.createToken('user-1');
470
-
471
- // Initially valid
472
- expect(service.isValid(token)).toBe(true);
473
-
474
- // Advance time by 59 minutes - still valid
475
- jest.advanceTimersByTime(59 * 60 * 1000);
476
- expect(service.isValid(token)).toBe(true);
477
-
478
- // Advance time by 2 more minutes - now expired
479
- jest.advanceTimersByTime(2 * 60 * 1000);
480
- expect(service.isValid(token)).toBe(false);
481
- });
72
+ // PARAMETERIZED TESTS
73
+ it.each([
74
+ ['simple@example.com', true],
75
+ ['user.name@domain.org', true],
76
+ ['invalid', false],
77
+ ['@nodomain.com', false],
78
+ ])('validates %s as %s', (email, expected) => {
79
+ expect(isValidEmail(email)).toBe(expected);
482
80
  });
483
81
 
484
- // ═══════════════════════════════════════════════════════════
485
- // Testing Async Code
486
- // ═══════════════════════════════════════════════════════════
487
-
488
- describe('NotificationService', () => {
489
- it('sends notification and waits for delivery', async () => {
490
- const service = new NotificationService();
491
-
492
- // Test async success
493
- const result = await service.send('Hello');
494
- expect(result.delivered).toBe(true);
495
- });
496
-
497
- it('handles async errors', async () => {
498
- const service = new NotificationService();
499
- service.setOffline(true);
500
-
501
- // Test async error
502
- await expect(service.send('Hello')).rejects.toThrow('Network unavailable');
503
- });
504
-
505
- it('retries failed deliveries', async () => {
506
- const mockTransport = {
507
- send: jest
508
- .fn()
509
- .mockRejectedValueOnce(new Error('Temporary failure'))
510
- .mockResolvedValueOnce({ success: true }),
511
- };
512
-
513
- const service = new NotificationService(mockTransport);
514
- await service.send('Hello');
515
-
516
- expect(mockTransport.send).toHaveBeenCalledTimes(2);
517
- });
518
- });
82
+ // BUILDER PATTERN for test data
83
+ const user = new UserBuilder().admin().inactive().build();
519
84
  ```
520
85
 
521
- ### 5. Coverage and Quality Metrics
522
-
523
- ```markdown
524
- ## Code Coverage Guidelines
525
-
526
- ### Coverage Targets by Type
527
-
528
- | Test Type | Target | Rationale |
529
- |-----------|--------|-----------|
530
- | Unit Tests | 80%+ | Core logic coverage |
531
- | Integration | 60%+ | Critical paths |
532
- | E2E | 100% critical | User journeys |
533
-
534
- ### What Coverage Tells You
535
- ✅ Code that IS executed by tests
536
- ❌ Does NOT mean code is well-tested
537
- ❌ Does NOT mean edge cases covered
538
- ❌ Does NOT mean behavior is verified
539
-
540
- ### Meaningful Coverage
541
- ```typescript
542
- // 100% coverage but NOT well-tested
543
- function divide(a: number, b: number): number {
544
- return a / b;
545
- }
546
-
547
- it('divides two numbers', () => {
548
- expect(divide(10, 2)).toBe(5); // 100% coverage!
549
- });
550
-
551
- // Missing tests:
552
- // - divide(10, 0) - Division by zero
553
- // - divide(0, 5) - Zero numerator
554
- // - divide(-10, 2) - Negative numbers
555
- // - divide(1.5, 0.3) - Floating point
556
86
  ```
557
-
558
- ### Coverage Report Analysis
559
- ```bash
560
- # Generate coverage report
561
- npm test -- --coverage
562
-
563
- # Example output:
564
- # -------------------|---------|----------|---------|---------|
565
- # File | % Stmts | % Branch | % Funcs | % Lines |
566
- # -------------------|---------|----------|---------|---------|
567
- # All files | 85.32 | 72.41 | 91.23 | 84.76 |
568
- # src/services | 92.14 | 85.00 | 95.00 | 91.89 |
569
- # UserService.ts | 95.00 | 90.00 | 100.00 | 94.74 |
570
- # OrderService.ts | 89.47 | 80.00 | 90.00 | 89.19 |
87
+ # Mocking Guidelines
88
+ MOCK (external boundaries):
89
+ - External APIs (payment, email)
90
+ - Third-party services
91
+ - System clock, random
92
+ - Network requests
93
+
94
+ DON'T MOCK (your code):
95
+ - Pure functions
96
+ - Data transformations
97
+ - Business logic
98
+ - Internal services
571
99
  ```
572
100
 
573
- ### Branch Coverage Focus
574
- ```typescript
575
- // Branch coverage example
576
- function processOrder(order: Order): Result {
577
- if (order.total > 1000) { // Branch 1
578
- if (order.customerType === 'vip') { // Branch 2
579
- return applyVipDiscount(order);
580
- }
581
- return applyBulkDiscount(order);
582
- }
583
- return processStandard(order);
584
- }
585
-
586
- // Tests needed for 100% branch coverage:
587
- // 1. order.total <= 1000
588
- // 2. order.total > 1000 AND customerType === 'vip'
589
- // 3. order.total > 1000 AND customerType !== 'vip'
590
101
  ```
591
-
592
- ### 6. TDD with Different Architectures
593
-
594
- ```typescript
595
- // ═══════════════════════════════════════════════════════════
596
- // TDD with Clean Architecture
597
- // ═══════════════════════════════════════════════════════════
598
-
599
- // 1. Start with USE CASE test
600
- describe('CreateUserUseCase', () => {
601
- it('creates user and sends welcome email', async () => {
602
- // Arrange
603
- const mockUserRepo: jest.Mocked<UserRepository> = {
604
- save: jest.fn().mockResolvedValue({ id: '1', email: 'test@test.com' }),
605
- findByEmail: jest.fn().mockResolvedValue(null),
606
- };
607
- const mockEmailService: jest.Mocked<EmailService> = {
608
- send: jest.fn().mockResolvedValue(true),
609
- };
610
-
611
- const useCase = new CreateUserUseCase(mockUserRepo, mockEmailService);
612
-
613
- // Act
614
- const result = await useCase.execute({
615
- email: 'test@test.com',
616
- password: 'password123',
617
- });
618
-
619
- // Assert
620
- expect(result.success).toBe(true);
621
- expect(mockUserRepo.save).toHaveBeenCalled();
622
- expect(mockEmailService.send).toHaveBeenCalledWith(
623
- 'test@test.com',
624
- expect.stringContaining('Welcome')
625
- );
626
- });
627
- });
628
-
629
- // 2. Then implement the use case
630
- class CreateUserUseCase {
631
- constructor(
632
- private userRepo: UserRepository,
633
- private emailService: EmailService
634
- ) {}
635
-
636
- async execute(input: CreateUserInput): Promise<CreateUserOutput> {
637
- // Check if user exists
638
- const existing = await this.userRepo.findByEmail(input.email);
639
- if (existing) {
640
- return { success: false, error: 'Email already registered' };
641
- }
642
-
643
- // Create user
644
- const user = await this.userRepo.save({
645
- email: input.email,
646
- passwordHash: await hash(input.password),
647
- });
648
-
649
- // Send welcome email
650
- await this.emailService.send(user.email, 'Welcome to our platform!');
651
-
652
- return { success: true, userId: user.id };
653
- }
654
- }
655
-
656
- // ═══════════════════════════════════════════════════════════
657
- // TDD with API Endpoints
658
- // ═══════════════════════════════════════════════════════════
659
-
660
- describe('POST /api/users', () => {
661
- it('creates user and returns 201', async () => {
662
- const response = await request(app)
663
- .post('/api/users')
664
- .send({
665
- email: 'new@example.com',
666
- password: 'securepass123',
667
- })
668
- .expect(201);
669
-
670
- expect(response.body).toMatchObject({
671
- id: expect.any(String),
672
- email: 'new@example.com',
673
- });
674
- });
675
-
676
- it('returns 400 for invalid email', async () => {
677
- const response = await request(app)
678
- .post('/api/users')
679
- .send({
680
- email: 'not-an-email',
681
- password: 'securepass123',
682
- })
683
- .expect(400);
684
-
685
- expect(response.body.error).toContain('email');
686
- });
687
-
688
- it('returns 409 for duplicate email', async () => {
689
- // Create user first
690
- await createUser({ email: 'exists@example.com' });
691
-
692
- // Try to create duplicate
693
- const response = await request(app)
694
- .post('/api/users')
695
- .send({
696
- email: 'exists@example.com',
697
- password: 'securepass123',
698
- })
699
- .expect(409);
700
-
701
- expect(response.body.error).toContain('already exists');
702
- });
703
- });
704
- ```
705
-
706
- ## Use Cases
707
-
708
- ### Building a Calculator with TDD
709
-
710
- ```typescript
711
- // Progressive TDD session building a calculator
712
-
713
- // Test 1: Addition
714
- describe('Calculator', () => {
715
- describe('add', () => {
716
- it('adds two positive numbers', () => {
717
- expect(add(2, 3)).toBe(5);
718
- });
719
- });
720
- });
721
-
722
- // Implementation 1
723
- function add(a: number, b: number): number {
724
- return a + b;
725
- }
726
-
727
- // Test 2: Negative numbers
728
- it('adds negative numbers', () => {
729
- expect(add(-2, -3)).toBe(-5);
730
- expect(add(-2, 3)).toBe(1);
731
- });
732
- // Already passes!
733
-
734
- // Test 3: Subtraction
735
- describe('subtract', () => {
736
- it('subtracts two numbers', () => {
737
- expect(subtract(5, 3)).toBe(2);
738
- });
739
- });
740
-
741
- // Implementation 3
742
- function subtract(a: number, b: number): number {
743
- return a - b;
744
- }
745
-
746
- // Refactor: Extract to class
747
- class Calculator {
748
- add(a: number, b: number): number {
749
- return a + b;
750
- }
751
-
752
- subtract(a: number, b: number): number {
753
- return a - b;
754
- }
755
- }
756
-
757
- // Continue with multiply, divide, etc.
102
+ # Coverage Analysis
103
+ | Type | Target | Notes |
104
+ |------|--------|-------|
105
+ | Statements | 80%+ | Code executed |
106
+ | Branches | 70%+ | If/else paths |
107
+ | Functions | 90%+ | All functions called |
108
+
109
+ REMEMBER: 100% coverage != well-tested
110
+ - Test edge cases
111
+ - Test error paths
112
+ - Test boundary conditions
758
113
  ```
759
114
 
760
115
  ## Best Practices
761
116
 
762
- ### Do's
763
-
764
- - Write the test BEFORE the implementation
765
- - Keep tests focused on ONE behavior
766
- - Use descriptive test names that document behavior
767
- - Run tests frequently (every few minutes)
768
- - Refactor only when tests are green
769
- - Test behavior, not implementation
770
- - Keep the Red-Green-Refactor cycle short
771
- - Use test doubles (mocks) for external dependencies
772
- - Organize tests to mirror source structure
773
- - Delete tests that no longer add value
774
-
775
- ### Don'ts
776
-
777
- - Don't write implementation before tests
778
- - Don't test private methods directly
779
- - Don't mock what you don't own
780
- - Don't skip the refactor step
781
- - Don't write tests for trivial code
782
- - Don't let tests become too slow
783
- - Don't test framework/library code
784
- - Don't ignore flaky tests
785
- - Don't over-mock (test integration too)
786
- - Don't forget to run all tests before commit
787
-
788
- ## References
789
-
790
- - [Test Driven Development by Kent Beck](https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530)
791
- - [Growing Object-Oriented Software, Guided by Tests](http://www.growing-object-oriented-software.com/)
792
- - [The Art of Unit Testing](https://www.manning.com/books/the-art-of-unit-testing-third-edition)
793
- - [Jest Documentation](https://jestjs.io/docs/getting-started)
794
- - [Testing Library](https://testing-library.com/)
117
+ | Do | Avoid |
118
+ |----|-------|
119
+ | Write test BEFORE implementation | Writing implementation first |
120
+ | Keep tests focused on ONE behavior | Testing multiple things in one test |
121
+ | Use descriptive test names | Generic names like "test1" |
122
+ | Run tests frequently (every few minutes) | Long gaps between test runs |
123
+ | Refactor only when tests green | Refactoring with failing tests |
124
+ | Test behavior, not implementation | Testing private methods directly |
125
+ | Keep cycles short (1-5 minutes) | 30+ minute cycles |
126
+ | Mock only external dependencies | Over-mocking your own code |
127
+
128
+ ## Related Skills
129
+
130
+ - `avoiding-testing-anti-patterns` - Avoid common test mistakes
131
+ - `testing-with-vitest` - Vitest testing framework
132
+ - `testing-with-playwright` - E2E testing with Playwright
133
+ - `verifying-before-completion` - Ensure test quality