omgkit 2.1.1 → 2.2.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 (50) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  5. package/plugin/skills/databases/redis/SKILL.md +885 -25
  6. package/plugin/skills/devops/aws/SKILL.md +686 -28
  7. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  8. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  9. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  10. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  12. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  14. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  15. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  16. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  17. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  18. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  19. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  20. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  21. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  22. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  23. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  24. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  25. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  26. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  27. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  28. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  29. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  30. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  31. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  32. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  33. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  34. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  35. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  36. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  37. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  38. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  39. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  40. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  41. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  42. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  43. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  44. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  45. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  46. package/plugin/skills/security/oauth/SKILL.md +968 -31
  47. package/plugin/skills/security/owasp/SKILL.md +894 -33
  48. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  49. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  50. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,50 +1,712 @@
1
1
  ---
2
2
  name: testing-anti-patterns
3
- description: Testing anti-patterns to avoid. Use when writing or reviewing tests.
3
+ description: Common testing anti-patterns to avoid and how to fix them for reliable, maintainable test suites
4
+ category: methodology
5
+ triggers:
6
+ - testing anti-patterns
7
+ - flaky tests
8
+ - test smells
9
+ - bad tests
10
+ - test maintenance
11
+ - unreliable tests
12
+ - test code quality
4
13
  ---
5
14
 
6
- # Testing Anti-Patterns Skill
15
+ # Testing Anti-Patterns
7
16
 
8
- ## Anti-Patterns
17
+ Identify and fix **common testing anti-patterns** that lead to flaky, slow, or unmaintainable test suites. This skill provides patterns to recognize problematic tests and techniques to refactor them into reliable, valuable tests.
9
18
 
10
- ### 1. Testing Implementation
11
- ```typescript
12
- // Bad - tests internal implementation
13
- expect(component.state.count).toBe(1);
19
+ ## Purpose
20
+
21
+ Build reliable, maintainable test suites:
22
+
23
+ - Recognize testing anti-patterns quickly
24
+ - Fix flaky tests that undermine confidence
25
+ - Avoid tests that test implementation details
26
+ - Eliminate slow tests that hurt development velocity
27
+ - Remove tests that are hard to maintain
28
+ - Build tests that provide real value
29
+ - Create self-documenting test suites
30
+
31
+ ## Features
32
+
33
+ ### 1. The Testing Anti-Pattern Catalog
14
34
 
15
- // Good - tests behavior
16
- expect(screen.getByText('1')).toBeInTheDocument();
35
+ ```markdown
36
+ ## Common Testing Anti-Patterns
37
+
38
+ ┌─────────────────────────────────────────────────────────────────────────┐
39
+ │ TESTING ANTI-PATTERN SEVERITY │
40
+ ├─────────────────────────────────────────────────────────────────────────┤
41
+ │ │
42
+ │ 🔴 CRITICAL (Fix immediately) │
43
+ │ ───────────────────────────── │
44
+ │ • Flaky tests - Random failures destroy trust │
45
+ │ • Testing implementation - Breaks on every refactor │
46
+ │ • Hidden dependencies - Tests fail mysteriously │
47
+ │ │
48
+ │ 🟡 HIGH (Fix soon) │
49
+ │ ───────────────── │
50
+ │ • Slow tests - Hurt development velocity │
51
+ │ • Test interdependence - Can't run tests in isolation │
52
+ │ • Over-mocking - Tests pass but bugs slip through │
53
+ │ │
54
+ │ 🟠 MEDIUM (Plan to fix) │
55
+ │ ─────────────────────── │
56
+ │ • Poor naming - Tests don't document behavior │
57
+ │ • Magic values - Unclear why values are expected │
58
+ │ • Giant tests - Hard to understand and maintain │
59
+ │ │
60
+ │ 🔵 LOW (Fix when touching) │
61
+ │ ───────────────────────── │
62
+ │ • Commented tests - Remove or fix them │
63
+ │ • Duplicate tests - Consolidate coverage │
64
+ │ • Dead assertions - Remove or make meaningful │
65
+ │ │
66
+ └─────────────────────────────────────────────────────────────────────────┘
17
67
  ```
18
68
 
19
69
  ### 2. Flaky Tests
70
+
71
+ ```typescript
72
+ /**
73
+ * ANTI-PATTERN: Flaky Tests
74
+ * Tests that sometimes pass, sometimes fail
75
+ */
76
+
77
+ // ❌ FLAKY: Timing-dependent test
78
+ describe('FlakY: Timing Issues', () => {
79
+ it('processes data after delay', async () => {
80
+ startAsyncProcess();
81
+
82
+ // PROBLEM: 100ms might not be enough on slow CI
83
+ await sleep(100);
84
+
85
+ expect(result).toBe('processed');
86
+ });
87
+ });
88
+
89
+ // ✅ FIXED: Wait for condition, not time
90
+ describe('Fixed: Proper async handling', () => {
91
+ it('processes data after delay', async () => {
92
+ startAsyncProcess();
93
+
94
+ // Wait for actual condition
95
+ await waitFor(() => {
96
+ expect(result).toBe('processed');
97
+ }, { timeout: 5000 });
98
+ });
99
+ });
100
+
101
+ // ❌ FLAKY: Random data without determinism
102
+ describe('Flaky: Random Data', () => {
103
+ it('handles user data', () => {
104
+ const user = {
105
+ id: Math.random().toString(),
106
+ name: faker.name.fullName()
107
+ };
108
+
109
+ const result = processUser(user);
110
+
111
+ // PROBLEM: Can't know expected result
112
+ expect(result).toBeDefined();
113
+ });
114
+ });
115
+
116
+ // ✅ FIXED: Deterministic test data
117
+ describe('Fixed: Deterministic Data', () => {
118
+ it('handles user data', () => {
119
+ const user = {
120
+ id: 'user-123',
121
+ name: 'John Doe'
122
+ };
123
+
124
+ const result = processUser(user);
125
+
126
+ expect(result).toEqual({
127
+ id: 'user-123',
128
+ displayName: 'John Doe',
129
+ slug: 'john-doe'
130
+ });
131
+ });
132
+ });
133
+
134
+ // ❌ FLAKY: Order-dependent tests
135
+ describe('Flaky: Order Dependent', () => {
136
+ let sharedState = [];
137
+
138
+ it('adds item', () => {
139
+ sharedState.push('item1');
140
+ expect(sharedState).toHaveLength(1);
141
+ });
142
+
143
+ it('has two items', () => {
144
+ // PROBLEM: Depends on previous test running first
145
+ sharedState.push('item2');
146
+ expect(sharedState).toHaveLength(2);
147
+ });
148
+ });
149
+
150
+ // ✅ FIXED: Independent tests
151
+ describe('Fixed: Independent Tests', () => {
152
+ let sharedState: string[];
153
+
154
+ beforeEach(() => {
155
+ sharedState = []; // Fresh state each test
156
+ });
157
+
158
+ it('adds first item', () => {
159
+ sharedState.push('item1');
160
+ expect(sharedState).toHaveLength(1);
161
+ });
162
+
163
+ it('adds second item', () => {
164
+ sharedState.push('item2');
165
+ expect(sharedState).toHaveLength(1); // Independent!
166
+ });
167
+ });
168
+ ```
169
+
170
+ ### 3. Testing Implementation Details
171
+
172
+ ```typescript
173
+ /**
174
+ * ANTI-PATTERN: Testing Implementation
175
+ * Tests break when implementation changes, even if behavior is correct
176
+ */
177
+
178
+ // ❌ BAD: Testing internal state
179
+ describe('Anti-pattern: Internal State', () => {
180
+ it('sets internal counter', () => {
181
+ const counter = new Counter();
182
+ counter.increment();
183
+
184
+ // PROBLEM: Testing private implementation
185
+ expect(counter._count).toBe(1);
186
+ });
187
+ });
188
+
189
+ // ✅ GOOD: Testing behavior
190
+ describe('Pattern: Test Behavior', () => {
191
+ it('increments and returns count', () => {
192
+ const counter = new Counter();
193
+
194
+ expect(counter.increment()).toBe(1);
195
+ expect(counter.getValue()).toBe(1);
196
+ });
197
+ });
198
+
199
+ // ❌ BAD: Testing function calls
200
+ describe('Anti-pattern: Spy Everything', () => {
201
+ it('calls internal methods', () => {
202
+ const service = new UserService();
203
+ const validateSpy = vi.spyOn(service, 'validateEmail');
204
+ const hashSpy = vi.spyOn(service, 'hashPassword');
205
+
206
+ service.createUser({ email: 'test@test.com', password: '123' });
207
+
208
+ // PROBLEM: Tightly coupled to implementation
209
+ expect(validateSpy).toHaveBeenCalledWith('test@test.com');
210
+ expect(hashSpy).toHaveBeenCalledWith('123');
211
+ });
212
+ });
213
+
214
+ // ✅ GOOD: Testing outcomes
215
+ describe('Pattern: Test Outcomes', () => {
216
+ it('creates user with hashed password', async () => {
217
+ const service = new UserService();
218
+
219
+ const user = await service.createUser({
220
+ email: 'test@test.com',
221
+ password: '123'
222
+ });
223
+
224
+ // Test the result, not how we got there
225
+ expect(user.email).toBe('test@test.com');
226
+ expect(user.password).not.toBe('123'); // Hashed
227
+ expect(user.password).toMatch(/^\$2[aby]?\$/); // bcrypt format
228
+ });
229
+ });
230
+
231
+ // ❌ BAD: Testing component internals (React)
232
+ describe('Anti-pattern: Component Internals', () => {
233
+ it('sets state on click', () => {
234
+ const wrapper = shallow(<Counter />);
235
+
236
+ wrapper.find('button').simulate('click');
237
+
238
+ // PROBLEM: Testing React implementation
239
+ expect(wrapper.state('count')).toBe(1);
240
+ });
241
+ });
242
+
243
+ // ✅ GOOD: Testing user-facing behavior
244
+ describe('Pattern: User Behavior', () => {
245
+ it('shows incremented count on click', () => {
246
+ render(<Counter />);
247
+
248
+ const button = screen.getByRole('button', { name: /increment/i });
249
+ fireEvent.click(button);
250
+
251
+ // Test what user sees
252
+ expect(screen.getByText('Count: 1')).toBeInTheDocument();
253
+ });
254
+ });
255
+ ```
256
+
257
+ ### 4. Over-Mocking
258
+
259
+ ```typescript
260
+ /**
261
+ * ANTI-PATTERN: Over-Mocking
262
+ * Mocking so much that tests pass but bugs slip through
263
+ */
264
+
265
+ // ❌ BAD: Everything is mocked
266
+ describe('Anti-pattern: Mock Everything', () => {
267
+ it('processes order', async () => {
268
+ const mockDb = { save: vi.fn() };
269
+ const mockPayment = { charge: vi.fn().mockResolvedValue({ success: true }) };
270
+ const mockEmail = { send: vi.fn() };
271
+ const mockInventory = { reserve: vi.fn().mockResolvedValue(true) };
272
+ const mockShipping = { calculate: vi.fn().mockReturnValue(5.99) };
273
+
274
+ const service = new OrderService({
275
+ db: mockDb,
276
+ payment: mockPayment,
277
+ email: mockEmail,
278
+ inventory: mockInventory,
279
+ shipping: mockShipping
280
+ });
281
+
282
+ await service.processOrder(mockOrder);
283
+
284
+ // PROBLEM: We're only testing that our mocks were called
285
+ // The actual integration could be completely broken
286
+ expect(mockPayment.charge).toHaveBeenCalled();
287
+ expect(mockDb.save).toHaveBeenCalled();
288
+ });
289
+ });
290
+
291
+ // ✅ GOOD: Mock boundaries, not internals
292
+ describe('Pattern: Mock External Boundaries', () => {
293
+ let service: OrderService;
294
+ let testDb: TestDatabase;
295
+
296
+ beforeAll(async () => {
297
+ testDb = await createTestDatabase();
298
+ });
299
+
300
+ beforeEach(async () => {
301
+ // Use real database, mock only external services
302
+ service = new OrderService({
303
+ db: testDb,
304
+ payment: createMockPaymentProvider(), // External API
305
+ email: createMockEmailProvider(), // External service
306
+ inventory: new InventoryService(testDb), // Real, uses test DB
307
+ shipping: new ShippingCalculator() // Real, pure logic
308
+ });
309
+ });
310
+
311
+ it('processes order end-to-end', async () => {
312
+ const order = await service.processOrder({
313
+ items: [{ productId: 'prod-1', quantity: 2 }],
314
+ customer: testCustomer
315
+ });
316
+
317
+ // Real assertions on real behavior
318
+ expect(order.status).toBe('confirmed');
319
+ expect(order.total).toBe(29.98);
320
+
321
+ // Verify real database state
322
+ const dbOrder = await testDb.orders.findById(order.id);
323
+ expect(dbOrder.status).toBe('confirmed');
324
+ });
325
+ });
326
+
327
+ // The Mock Boundary Principle
328
+ const mockBoundaryGuidelines = {
329
+ mock: [
330
+ 'External APIs (payment, email, SMS)',
331
+ 'Third-party services',
332
+ 'System clock (for determinism)',
333
+ 'Random number generators',
334
+ 'Network requests'
335
+ ],
336
+ dontMock: [
337
+ 'Your own code (usually)',
338
+ 'Pure functions',
339
+ 'Data transformations',
340
+ 'Business logic',
341
+ 'Internal services (use test doubles with real behavior)'
342
+ ]
343
+ };
344
+ ```
345
+
346
+ ### 5. Slow Tests
347
+
348
+ ```typescript
349
+ /**
350
+ * ANTI-PATTERN: Slow Tests
351
+ * Tests that take too long, hurting development velocity
352
+ */
353
+
354
+ // ❌ SLOW: Unnecessary database for unit test
355
+ describe('Anti-pattern: Wrong Layer', () => {
356
+ it('validates email format', async () => {
357
+ // PROBLEM: Spinning up DB just to test string validation
358
+ const db = await createDatabase();
359
+ const service = new UserService(db);
360
+
361
+ const result = service.validateEmail('invalid');
362
+
363
+ expect(result).toBe(false);
364
+ await db.close();
365
+ });
366
+ });
367
+
368
+ // ✅ FAST: Test pure logic directly
369
+ describe('Pattern: Right Layer', () => {
370
+ it('validates email format', () => {
371
+ expect(validateEmail('invalid')).toBe(false);
372
+ expect(validateEmail('valid@example.com')).toBe(true);
373
+ });
374
+ });
375
+
376
+ // ❌ SLOW: Redundant setup
377
+ describe('Anti-pattern: Heavy Setup', () => {
378
+ it('test 1', async () => {
379
+ const db = await createDatabase();
380
+ await seedUsers(100);
381
+ await seedProducts(1000);
382
+ await seedOrders(500);
383
+
384
+ const result = await getUser('user-1');
385
+ expect(result.name).toBe('User 1');
386
+ });
387
+
388
+ it('test 2', async () => {
389
+ // Same heavy setup repeated!
390
+ const db = await createDatabase();
391
+ await seedUsers(100);
392
+ await seedProducts(1000);
393
+ await seedOrders(500);
394
+
395
+ const result = await getUser('user-2');
396
+ expect(result.name).toBe('User 2');
397
+ });
398
+ });
399
+
400
+ // ✅ FAST: Shared setup, minimal data
401
+ describe('Pattern: Efficient Setup', () => {
402
+ let db: TestDatabase;
403
+
404
+ beforeAll(async () => {
405
+ db = await createDatabase();
406
+ // Seed only what's needed for this suite
407
+ await seedUsers(3);
408
+ });
409
+
410
+ afterAll(async () => {
411
+ await db.close();
412
+ });
413
+
414
+ it('gets user 1', async () => {
415
+ const result = await getUser('user-1');
416
+ expect(result.name).toBe('User 1');
417
+ });
418
+
419
+ it('gets user 2', async () => {
420
+ const result = await getUser('user-2');
421
+ expect(result.name).toBe('User 2');
422
+ });
423
+ });
424
+
425
+ // ❌ SLOW: Real network in unit tests
426
+ describe('Anti-pattern: Real Network', () => {
427
+ it('fetches user from API', async () => {
428
+ // PROBLEM: Hits real API, slow and flaky
429
+ const response = await fetch('https://api.example.com/users/1');
430
+ const user = await response.json();
431
+
432
+ expect(user.name).toBe('John');
433
+ });
434
+ });
435
+
436
+ // ✅ FAST: Mock network
437
+ describe('Pattern: Mock Network', () => {
438
+ beforeEach(() => {
439
+ fetchMock.mockResponse(JSON.stringify({ name: 'John' }));
440
+ });
441
+
442
+ it('fetches user from API', async () => {
443
+ const user = await fetchUser(1);
444
+ expect(user.name).toBe('John');
445
+ });
446
+ });
447
+ ```
448
+
449
+ ### 6. Poor Test Design
450
+
20
451
  ```typescript
21
- // Bad - timing dependent
22
- await sleep(1000);
23
- expect(result).toBe(true);
452
+ /**
453
+ * ANTI-PATTERN: Poor Test Design
454
+ * Tests that are hard to understand or maintain
455
+ */
456
+
457
+ // ❌ BAD: Giant test with many assertions
458
+ describe('Anti-pattern: Giant Test', () => {
459
+ it('does everything', async () => {
460
+ const service = new OrderService();
461
+
462
+ // Create user
463
+ const user = await service.createUser({ name: 'John' });
464
+ expect(user.id).toBeDefined();
465
+
466
+ // Add payment method
467
+ await service.addPaymentMethod(user.id, { type: 'card' });
468
+ expect(user.paymentMethods).toHaveLength(1);
469
+
470
+ // Create order
471
+ const order = await service.createOrder(user.id, [{ id: 'p1' }]);
472
+ expect(order.status).toBe('pending');
473
+
474
+ // Process payment
475
+ await service.processPayment(order.id);
476
+ expect(order.status).toBe('paid');
477
+
478
+ // Ship order
479
+ await service.shipOrder(order.id);
480
+ expect(order.status).toBe('shipped');
481
+
482
+ // ... 20 more steps
483
+ // PROBLEM: One failure, no idea where
484
+ });
485
+ });
486
+
487
+ // ✅ GOOD: Focused tests
488
+ describe('Pattern: Focused Tests', () => {
489
+ describe('Order Creation', () => {
490
+ it('creates pending order from cart items', async () => {
491
+ const order = await service.createOrder(user.id, cartItems);
492
+
493
+ expect(order.status).toBe('pending');
494
+ expect(order.items).toEqual(cartItems);
495
+ });
496
+ });
497
+
498
+ describe('Payment Processing', () => {
499
+ it('marks order as paid after successful charge', async () => {
500
+ const order = await createPendingOrder();
501
+
502
+ await service.processPayment(order.id);
503
+
504
+ expect(await getOrder(order.id).status).toBe('paid');
505
+ });
506
+
507
+ it('keeps order pending if charge fails', async () => {
508
+ const order = await createPendingOrder();
509
+ mockPayment.failNextCharge();
24
510
 
25
- // Good - wait for condition
26
- await waitFor(() => expect(result).toBe(true));
511
+ await expect(
512
+ service.processPayment(order.id)
513
+ ).rejects.toThrow(PaymentError);
514
+
515
+ expect(await getOrder(order.id).status).toBe('pending');
516
+ });
517
+ });
518
+ });
519
+
520
+ // ❌ BAD: Magic values
521
+ describe('Anti-pattern: Magic Values', () => {
522
+ it('calculates discount', () => {
523
+ // PROBLEM: Why 150? Why 15?
524
+ expect(calculateDiscount(150)).toBe(15);
525
+ expect(calculateDiscount(99)).toBe(0);
526
+ });
527
+ });
528
+
529
+ // ✅ GOOD: Self-documenting values
530
+ describe('Pattern: Clear Intent', () => {
531
+ it('applies 10% discount for orders over $100', () => {
532
+ const DISCOUNT_THRESHOLD = 100;
533
+ const DISCOUNT_RATE = 0.10;
534
+
535
+ const orderAboveThreshold = 150;
536
+ const orderBelowThreshold = 99;
537
+
538
+ expect(calculateDiscount(orderAboveThreshold))
539
+ .toBe(orderAboveThreshold * DISCOUNT_RATE);
540
+
541
+ expect(calculateDiscount(orderBelowThreshold))
542
+ .toBe(0);
543
+ });
544
+ });
545
+
546
+ // ❌ BAD: Unclear test names
547
+ describe('Anti-pattern: Bad Names', () => {
548
+ it('test1', () => { /* ... */ });
549
+ it('should work', () => { /* ... */ });
550
+ it('handles edge case', () => { /* ... */ });
551
+ });
552
+
553
+ // ✅ GOOD: Behavior-describing names
554
+ describe('Pattern: Descriptive Names', () => {
555
+ it('returns empty array when no products match filter', () => { /* ... */ });
556
+ it('throws InvalidEmailError for malformed email addresses', () => { /* ... */ });
557
+ it('retries up to 3 times before failing', () => { /* ... */ });
558
+ });
27
559
  ```
28
560
 
29
- ### 3. Test Interdependence
561
+ ### 7. Test Smells Detection
562
+
30
563
  ```typescript
31
- // Bad - tests share state
32
- let user;
33
- it('creates user', () => { user = create(); });
34
- it('uses user', () => { expect(user).toBe(...); });
564
+ /**
565
+ * Automated test smell detection
566
+ */
567
+
568
+ interface TestSmell {
569
+ type: string;
570
+ severity: 'critical' | 'high' | 'medium' | 'low';
571
+ pattern: RegExp;
572
+ fix: string;
573
+ }
574
+
575
+ const testSmells: TestSmell[] = [
576
+ {
577
+ type: 'sleep-in-test',
578
+ severity: 'critical',
579
+ pattern: /await\s+sleep\s*\(/,
580
+ fix: 'Use waitFor or proper async handling'
581
+ },
582
+ {
583
+ type: 'implementation-testing',
584
+ severity: 'high',
585
+ pattern: /\._\w+|\.state\(|\.instance\(\)/,
586
+ fix: 'Test behavior through public interface'
587
+ },
588
+ {
589
+ type: 'broad-assertion',
590
+ severity: 'medium',
591
+ pattern: /expect\([^)]+\)\.toBeDefined\(\)/,
592
+ fix: 'Use specific assertions'
593
+ },
594
+ {
595
+ type: 'magic-number',
596
+ severity: 'medium',
597
+ pattern: /expect\([^)]+\)\.toBe\(\d{3,}\)/,
598
+ fix: 'Use named constants'
599
+ },
600
+ {
601
+ type: 'commented-test',
602
+ severity: 'low',
603
+ pattern: /\/\/\s*it\(|\/\*[\s\S]*it\(/,
604
+ fix: 'Remove or fix commented tests'
605
+ }
606
+ ];
607
+
608
+ function detectTestSmells(testCode: string): DetectedSmell[] {
609
+ const smells: DetectedSmell[] = [];
35
610
 
36
- // Good - independent tests
37
- beforeEach(() => { user = create(); });
611
+ for (const smell of testSmells) {
612
+ const matches = testCode.matchAll(new RegExp(smell.pattern, 'g'));
613
+ for (const match of matches) {
614
+ smells.push({
615
+ ...smell,
616
+ location: match.index,
617
+ snippet: match[0]
618
+ });
619
+ }
620
+ }
621
+
622
+ return smells;
623
+ }
38
624
  ```
39
625
 
40
- ### 4. Overmocking
626
+ ## Use Cases
627
+
628
+ ### Refactoring a Flaky Test Suite
629
+
41
630
  ```typescript
42
- // Bad - mock everything
43
- // Good - use real implementations when practical
631
+ // Before: Flaky, slow, hard to maintain
632
+ describe('OrderService (before)', () => {
633
+ it('processes order', async () => {
634
+ const db = await connectToRealDatabase();
635
+ const order = await createOrder(db);
636
+
637
+ // Flaky: timing
638
+ await sleep(500);
639
+
640
+ // Implementation detail
641
+ expect(order._internalState).toBe('processing');
642
+
643
+ // Magic value
644
+ expect(order.total).toBe(127.45);
645
+ });
646
+ });
647
+
648
+ // After: Reliable, fast, clear
649
+ describe('OrderService (after)', () => {
650
+ let testDb: TestDatabase;
651
+ let service: OrderService;
652
+
653
+ beforeAll(async () => {
654
+ testDb = await createTestDatabase();
655
+ service = new OrderService(testDb);
656
+ });
657
+
658
+ it('calculates total from items', async () => {
659
+ const items = [
660
+ createTestItem({ price: 99.99 }),
661
+ createTestItem({ price: 27.46 })
662
+ ];
663
+
664
+ const order = await service.createOrder(items);
665
+
666
+ // Clear expectation
667
+ expect(order.total).toBe(127.45);
668
+ });
669
+
670
+ it('transitions to processing after payment', async () => {
671
+ const order = await createPendingOrder();
672
+
673
+ await service.processPayment(order.id);
674
+
675
+ // Test behavior, not implementation
676
+ await waitFor(() => {
677
+ expect(service.getOrderStatus(order.id)).toBe('processing');
678
+ });
679
+ });
680
+ });
44
681
  ```
45
682
 
46
- ## Guidelines
47
- - Test behavior, not implementation
48
- - Keep tests independent
49
- - Mock only external dependencies
50
- - Use realistic test data
683
+ ## Best Practices
684
+
685
+ ### Do's
686
+
687
+ - **Test behavior, not implementation** - what it does, not how
688
+ - **Use factories for test data** - consistent, typed data
689
+ - **Write descriptive test names** - document the behavior
690
+ - **Keep tests independent** - no shared mutable state
691
+ - **Mock only external boundaries** - APIs, not your code
692
+ - **Use proper async patterns** - waitFor, not sleep
693
+ - **Make assertions specific** - not just toBeDefined
694
+ - **Run tests in random order** - catch hidden dependencies
695
+
696
+ ### Don'ts
697
+
698
+ - Don't use sleep/setTimeout in tests
699
+ - Don't access private properties
700
+ - Don't share state between tests
701
+ - Don't mock everything
702
+ - Don't write 1000-line test files
703
+ - Don't use real network/databases in unit tests
704
+ - Don't ignore flaky tests
705
+ - Don't comment out failing tests
706
+
707
+ ## References
708
+
709
+ - [Testing Library Guiding Principles](https://testing-library.com/docs/guiding-principles)
710
+ - [Test Desiderata - Kent Beck](https://medium.com/@kentbeck_7670/test-desiderata-94150638a4b3)
711
+ - [xUnit Test Patterns](http://xunitpatterns.com/)
712
+ - [Growing Object-Oriented Software, Guided by Tests](http://www.growing-object-oriented-software.com/)