@grimoire-cc/cli 0.6.3 → 0.7.1

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 (34) hide show
  1. package/dist/commands/logs.d.ts.map +1 -1
  2. package/dist/commands/logs.js +2 -2
  3. package/dist/commands/logs.js.map +1 -1
  4. package/dist/static/log-viewer.html +946 -690
  5. package/dist/static/static/log-viewer.html +946 -690
  6. package/package.json +1 -1
  7. package/packs/dev-pack/agents/gr.code-reviewer.md +286 -0
  8. package/packs/dev-pack/agents/gr.tdd-specialist.md +44 -0
  9. package/packs/dev-pack/grimoire.json +55 -0
  10. package/packs/dev-pack/skills/gr.tdd-specialist/SKILL.md +247 -0
  11. package/packs/dev-pack/skills/gr.tdd-specialist/reference/anti-patterns.md +166 -0
  12. package/packs/dev-pack/skills/gr.tdd-specialist/reference/language-frameworks.md +388 -0
  13. package/packs/dev-pack/skills/gr.tdd-specialist/reference/tdd-workflow-patterns.md +135 -0
  14. package/packs/docs-pack/grimoire.json +30 -0
  15. package/packs/docs-pack/skills/gr.business-logic-docs/SKILL.md +278 -0
  16. package/packs/docs-pack/skills/gr.business-logic-docs/references/audit-checklist.md +48 -0
  17. package/packs/docs-pack/skills/gr.business-logic-docs/references/tier2-template.md +129 -0
  18. package/packs/essentials-pack/agents/gr.fact-checker.md +202 -0
  19. package/packs/essentials-pack/grimoire.json +12 -0
  20. package/packs/meta-pack/grimoire.json +72 -0
  21. package/packs/meta-pack/skills/gr.context-file-guide/SKILL.md +201 -0
  22. package/packs/meta-pack/skills/gr.context-file-guide/scripts/validate-context-file.sh +29 -0
  23. package/packs/meta-pack/skills/gr.readme-guide/SKILL.md +362 -0
  24. package/packs/meta-pack/skills/gr.skill-developer/SKILL.md +321 -0
  25. package/packs/meta-pack/skills/gr.skill-developer/examples/brand-guidelines.md +94 -0
  26. package/packs/meta-pack/skills/gr.skill-developer/examples/financial-analysis.md +85 -0
  27. package/packs/meta-pack/skills/gr.skill-developer/reference/best-practices.md +410 -0
  28. package/packs/meta-pack/skills/gr.skill-developer/reference/file-organization.md +452 -0
  29. package/packs/meta-pack/skills/gr.skill-developer/reference/patterns.md +459 -0
  30. package/packs/meta-pack/skills/gr.skill-developer/reference/yaml-spec.md +214 -0
  31. package/packs/meta-pack/skills/gr.skill-developer/scripts/create-skill.sh +210 -0
  32. package/packs/meta-pack/skills/gr.skill-developer/scripts/validate-skill.py +520 -0
  33. package/packs/meta-pack/skills/gr.skill-developer/templates/basic-skill.md +94 -0
  34. package/packs/meta-pack/skills/gr.skill-developer/templates/domain-skill.md +108 -0
@@ -0,0 +1,166 @@
1
+ # Testing Anti-Patterns
2
+
3
+ Common testing mistakes that reduce test value and increase maintenance cost. These are language-agnostic — they apply to any test framework.
4
+
5
+ ## Table of Contents
6
+
7
+ - [The Liar](#the-liar)
8
+ - [The Giant](#the-giant)
9
+ - [Excessive Setup](#excessive-setup)
10
+ - [The Slow Poke](#the-slow-poke)
11
+ - [The Peeping Tom](#the-peeping-tom)
12
+ - [The Mockery](#the-mockery)
13
+ - [The Inspector](#the-inspector)
14
+ - [The Flaky Test](#the-flaky-test)
15
+
16
+ ## The Liar
17
+
18
+ **What it is:** A test that passes but doesn't actually verify the behavior it claims to test. It gives false confidence.
19
+
20
+ **How to spot it:**
21
+ - Test name says "validates input" but assertions only check the return type
22
+ - Assertions are too loose (`assert result is not None` instead of checking the actual value)
23
+ - Test catches exceptions broadly and passes regardless
24
+
25
+ **Fix:** Ensure assertions directly verify the specific behavior described in the test name. Every assertion should fail if the behavior breaks.
26
+
27
+ ```python
28
+ # Bad — passes even if discount logic is completely wrong
29
+ def test_apply_discount():
30
+ result = apply_discount(100, 10)
31
+ assert result is not None
32
+
33
+ # Good — fails if the calculation is wrong
34
+ def test_apply_discount_with_10_percent_returns_90():
35
+ result = apply_discount(100, 10)
36
+ assert result == 90.0
37
+ ```
38
+
39
+ ## The Giant
40
+
41
+ **What it is:** A single test that verifies too many things. When it fails, you can't tell which behavior broke.
42
+
43
+ **How to spot it:**
44
+ - Test has more than 8-10 assertions
45
+ - Test name uses "and" (e.g., "creates user and sends email and updates cache")
46
+ - Multiple Act phases in one test
47
+
48
+ **Fix:** Split into focused tests, each verifying one logical concept. Multiple assertions are fine if they verify aspects of the same behavior.
49
+
50
+ ```typescript
51
+ // Bad — three unrelated behaviors in one test
52
+ test('user registration works', () => {
53
+ const user = register({ name: 'Alice', email: 'alice@test.com' });
54
+ expect(user.id).toBeDefined();
55
+ expect(emailService.send).toHaveBeenCalled();
56
+ expect(cache.set).toHaveBeenCalledWith(`user:${user.id}`, user);
57
+ expect(auditLog.entries).toHaveLength(1);
58
+ });
59
+
60
+ // Good — separate tests for each behavior
61
+ test('register with valid data creates user with id', () => { ... });
62
+ test('register with valid data sends welcome email', () => { ... });
63
+ test('register with valid data caches the user', () => { ... });
64
+ test('register with valid data writes audit log entry', () => { ... });
65
+ ```
66
+
67
+ ## Excessive Setup
68
+
69
+ **What it is:** Tests that require dozens of lines of setup before the actual test logic. Often signals that the code under test has too many dependencies.
70
+
71
+ **How to spot it:**
72
+ - Arrange section is 20+ lines
73
+ - Multiple mocks configured with complex behaviors
74
+ - Shared setup methods that configure things most tests don't need
75
+
76
+ **Fix:** Use factory methods/builders for test data. Consider whether the code under test needs refactoring to reduce dependencies. Only set up what the specific test needs.
77
+
78
+ ```go
79
+ // Bad — every test sets up the entire world
80
+ func TestProcessOrder(t *testing.T) {
81
+ db := setupDatabase()
82
+ cache := setupCache()
83
+ logger := setupLogger()
84
+ emailClient := setupEmailClient()
85
+ validator := NewValidator(db)
86
+ processor := NewProcessor(cache)
87
+ service := NewOrderService(db, cache, logger, emailClient, validator, processor)
88
+ // ... 10 more lines of setup
89
+ result, err := service.ProcessOrder(ctx, order)
90
+ assert.NoError(t, err)
91
+ }
92
+
93
+ // Good — factory method hides irrelevant details
94
+ func TestProcessOrder_WithValidOrder_Succeeds(t *testing.T) {
95
+ service := newTestOrderService(t)
96
+ result, err := service.ProcessOrder(ctx, validOrder())
97
+ assert.NoError(t, err)
98
+ assert.Equal(t, "processed", result.Status)
99
+ }
100
+ ```
101
+
102
+ ## The Slow Poke
103
+
104
+ **What it is:** Tests that are slow because they use real I/O, network calls, or sleeps. Slow tests get run less frequently and slow down the feedback loop.
105
+
106
+ **How to spot it:**
107
+ - `time.Sleep()`, `Thread.sleep()`, `setTimeout` in tests
108
+ - Real HTTP calls, database connections, file system operations
109
+ - Test suite takes more than a few seconds for unit tests
110
+
111
+ **Fix:** Mock external dependencies. Use fake implementations for I/O. Replace time-based waits with event-based synchronization.
112
+
113
+ ## The Peeping Tom
114
+
115
+ **What it is:** Tests that access private/internal state to verify behavior instead of testing through the public interface.
116
+
117
+ **How to spot it:**
118
+ - Reflection to access private fields
119
+ - Testing internal method calls instead of observable results
120
+ - Assertions on implementation details (internal data structures, private counters)
121
+
122
+ **Fix:** Test through the public API. If you can't verify behavior through the public interface, the class may need a design change (e.g., expose a query method or extract a collaborator).
123
+
124
+ ## The Mockery
125
+
126
+ **What it is:** Tests that mock so heavily that they're testing mock configurations rather than real behavior. Every dependency is mocked, including simple value objects.
127
+
128
+ **How to spot it:**
129
+ - More mock setup lines than actual test logic
130
+ - Mocking concrete classes, value objects, or data structures
131
+ - Test passes but the real system fails because mocks don't match reality
132
+
133
+ **Fix:** Only mock at system boundaries (external services, databases, clocks). Use real implementations for in-process collaborators when practical.
134
+
135
+ ## The Inspector
136
+
137
+ **What it is:** Tests that verify exact method calls and their order rather than outcomes. They break whenever the implementation changes, even if behavior is preserved.
138
+
139
+ **How to spot it:**
140
+ - `verify(mock, times(1)).method()` for every mock interaction
141
+ - Assertions on call order
142
+ - Test breaks when you refactor without changing behavior
143
+
144
+ **Fix:** Verify state (the result) rather than interactions (how it got there). Only verify interactions for side effects that ARE the behavior (e.g., "email was sent").
145
+
146
+ ```java
147
+ // Bad — breaks if implementation changes sort algorithm
148
+ verify(sorter, times(1)).quickSort(any());
149
+ verify(sorter, never()).mergeSort(any());
150
+
151
+ // Good — verifies the outcome
152
+ assertThat(result).isSortedAccordingTo(naturalOrder());
153
+ ```
154
+
155
+ ## The Flaky Test
156
+
157
+ **What it is:** Tests that pass and fail intermittently without code changes. They erode trust in the test suite.
158
+
159
+ **Common causes:**
160
+ - Time-dependent logic (`new Date()`, `time.Now()`)
161
+ - Random data without fixed seeds
162
+ - Shared mutable state between tests
163
+ - Race conditions in async tests
164
+ - Dependency on test execution order
165
+
166
+ **Fix:** Inject time as a dependency. Use fixed seeds for randomness. Ensure test isolation. Use proper async synchronization.
@@ -0,0 +1,388 @@
1
+ # Language & Framework Reference
2
+
3
+ Quick-reference for each language's test ecosystem. Use this to apply framework-specific patterns after detecting the project's language.
4
+
5
+ ## Table of Contents
6
+
7
+ - [JavaScript / TypeScript](#javascript--typescript)
8
+ - [Python](#python)
9
+ - [Go](#go)
10
+ - [Rust](#rust)
11
+ - [Java / Kotlin](#java--kotlin)
12
+ - [C# / .NET](#c--net)
13
+ - [Ruby](#ruby)
14
+
15
+ ## JavaScript / TypeScript
16
+
17
+ ### Frameworks
18
+
19
+ | Framework | When to Use | Key Feature |
20
+ |-----------|-------------|-------------|
21
+ | **Vitest** | Vite projects, new TS projects | Fast, ESM-native, Jest-compatible API |
22
+ | **Jest** | React (CRA), existing Jest projects | Mature ecosystem, wide adoption |
23
+ | **Mocha + Chai** | Legacy projects, custom setups | Flexible, pluggable |
24
+ | **Node test runner** | Node.js 20+, minimal deps | Built-in, zero dependencies |
25
+
26
+ ### Vitest / Jest Patterns
27
+
28
+ ```typescript
29
+ import { describe, test, expect, vi, beforeEach } from 'vitest';
30
+
31
+ describe('OrderService', () => {
32
+ let mockRepo: MockedObject<OrderRepository>;
33
+ let service: OrderService;
34
+
35
+ beforeEach(() => {
36
+ mockRepo = { save: vi.fn(), findById: vi.fn() };
37
+ service = new OrderService(mockRepo);
38
+ });
39
+
40
+ test('processOrder with valid order saves and returns id', async () => {
41
+ // Arrange
42
+ const order = createValidOrder();
43
+ mockRepo.save.mockResolvedValue({ id: '123' });
44
+
45
+ // Act
46
+ const result = await service.processOrder(order);
47
+
48
+ // Assert
49
+ expect(result.id).toBe('123');
50
+ expect(mockRepo.save).toHaveBeenCalledWith(order);
51
+ });
52
+
53
+ test('processOrder with invalid order throws ValidationError', async () => {
54
+ // Arrange
55
+ const order = createInvalidOrder();
56
+
57
+ // Act & Assert
58
+ await expect(service.processOrder(order)).rejects.toThrow(ValidationError);
59
+ });
60
+ });
61
+ ```
62
+
63
+ ### File Conventions
64
+
65
+ - `*.test.ts` / `*.spec.ts` (co-located or in `__tests__/`)
66
+ - `vitest.config.ts` or `jest.config.ts` for configuration
67
+
68
+ ## Python
69
+
70
+ ### Frameworks
71
+
72
+ | Framework | When to Use | Key Feature |
73
+ |-----------|-------------|-------------|
74
+ | **pytest** | Default choice, most projects | Fixtures, parametrize, plugins |
75
+ | **unittest** | stdlib only, legacy projects | Built-in, class-based |
76
+
77
+ ### pytest Patterns
78
+
79
+ ```python
80
+ import pytest
81
+ from unittest.mock import Mock, AsyncMock, patch
82
+
83
+ @pytest.fixture
84
+ def mock_repo():
85
+ repo = Mock(spec=OrderRepository)
86
+ repo.save = AsyncMock(return_value=Order(id="123"))
87
+ return repo
88
+
89
+ @pytest.fixture
90
+ def service(mock_repo):
91
+ return OrderService(repository=mock_repo)
92
+
93
+ async def test_process_order_with_valid_order_returns_id(service, mock_repo):
94
+ # Arrange
95
+ order = create_valid_order()
96
+
97
+ # Act
98
+ result = await service.process_order(order)
99
+
100
+ # Assert
101
+ assert result.id == "123"
102
+ mock_repo.save.assert_called_once_with(order)
103
+
104
+ async def test_process_order_with_invalid_order_raises_validation_error(service):
105
+ # Arrange
106
+ order = create_invalid_order()
107
+
108
+ # Act & Assert
109
+ with pytest.raises(ValidationError, match="items cannot be empty"):
110
+ await service.process_order(order)
111
+
112
+ @pytest.mark.parametrize("discount,expected", [
113
+ (0, 100.0),
114
+ (10, 90.0),
115
+ (50, 50.0),
116
+ ])
117
+ def test_apply_discount_calculates_correctly(discount, expected):
118
+ assert apply_discount(100.0, discount) == expected
119
+ ```
120
+
121
+ ### File Conventions
122
+
123
+ - `test_*.py` or `*_test.py` in `tests/` directory
124
+ - `conftest.py` for shared fixtures
125
+ - `pytest.ini` or `[tool.pytest.ini_options]` in `pyproject.toml`
126
+
127
+ ## Go
128
+
129
+ ### Frameworks
130
+
131
+ | Framework | When to Use | Key Feature |
132
+ |-----------|-------------|-------------|
133
+ | **testing** (stdlib) | Always available | Built-in, no dependencies |
134
+ | **testify** | Assertions + mocking | `assert`, `require`, `mock` packages |
135
+ | **gomock** | Interface mocking | Code generation, strict expectations |
136
+
137
+ ### Go Patterns
138
+
139
+ ```go
140
+ func TestProcessOrder_WithValidOrder_ReturnsID(t *testing.T) {
141
+ // Arrange
142
+ repo := new(MockOrderRepository)
143
+ repo.On("Save", mock.Anything).Return(&Order{ID: "123"}, nil)
144
+ service := NewOrderService(repo)
145
+
146
+ // Act
147
+ result, err := service.ProcessOrder(context.Background(), validOrder)
148
+
149
+ // Assert
150
+ require.NoError(t, err)
151
+ assert.Equal(t, "123", result.ID)
152
+ repo.AssertExpectations(t)
153
+ }
154
+
155
+ func TestProcessOrder_WithInvalidOrder_ReturnsError(t *testing.T) {
156
+ // Arrange
157
+ service := NewOrderService(nil)
158
+
159
+ // Act
160
+ _, err := service.ProcessOrder(context.Background(), invalidOrder)
161
+
162
+ // Assert
163
+ assert.ErrorIs(t, err, ErrValidation)
164
+ }
165
+
166
+ // Table-driven tests (Go idiom)
167
+ func TestApplyDiscount(t *testing.T) {
168
+ tests := []struct {
169
+ name string
170
+ price float64
171
+ discount int
172
+ expected float64
173
+ }{
174
+ {"no discount", 100.0, 0, 100.0},
175
+ {"10 percent", 100.0, 10, 90.0},
176
+ {"50 percent", 100.0, 50, 50.0},
177
+ }
178
+ for _, tt := range tests {
179
+ t.Run(tt.name, func(t *testing.T) {
180
+ result := ApplyDiscount(tt.price, tt.discount)
181
+ assert.Equal(t, tt.expected, result)
182
+ })
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### File Conventions
188
+
189
+ - `*_test.go` in the same package (or `_test` package for black-box)
190
+ - `go test ./...` to run all tests
191
+ - `testdata/` for test fixtures
192
+
193
+ ## Rust
194
+
195
+ ### Patterns
196
+
197
+ ```rust
198
+ #[cfg(test)]
199
+ mod tests {
200
+ use super::*;
201
+ use mockall::predicate::*;
202
+
203
+ #[test]
204
+ fn process_order_with_valid_order_returns_id() {
205
+ // Arrange
206
+ let mut mock_repo = MockOrderRepository::new();
207
+ mock_repo.expect_save()
208
+ .returning(|_| Ok(Order { id: "123".into() }));
209
+ let service = OrderService::new(Box::new(mock_repo));
210
+
211
+ // Act
212
+ let result = service.process_order(&valid_order());
213
+
214
+ // Assert
215
+ assert_eq!(result.unwrap().id, "123");
216
+ }
217
+
218
+ #[test]
219
+ fn process_order_with_invalid_order_returns_error() {
220
+ // Arrange
221
+ let service = OrderService::new(Box::new(MockOrderRepository::new()));
222
+
223
+ // Act
224
+ let result = service.process_order(&invalid_order());
225
+
226
+ // Assert
227
+ assert!(matches!(result, Err(ServiceError::Validation(_))));
228
+ }
229
+ }
230
+ ```
231
+
232
+ ### File Conventions
233
+
234
+ - Tests in `#[cfg(test)] mod tests` at bottom of source file (unit tests)
235
+ - Integration tests in `tests/` directory
236
+ - `cargo test` to run all
237
+
238
+ ## Java / Kotlin
239
+
240
+ ### Frameworks
241
+
242
+ | Framework | When to Use | Key Feature |
243
+ |-----------|-------------|-------------|
244
+ | **JUnit 5** | Default choice | Modern, extensible, parameterized |
245
+ | **Mockito** | Mocking | Intuitive API, wide adoption |
246
+ | **AssertJ** | Fluent assertions | Readable, discoverable API |
247
+
248
+ ### JUnit 5 + Mockito Patterns
249
+
250
+ ```java
251
+ @ExtendWith(MockitoExtension.class)
252
+ class OrderServiceTest {
253
+ @Mock OrderRepository repository;
254
+ @InjectMocks OrderService service;
255
+
256
+ @Test
257
+ void processOrder_withValidOrder_returnsId() {
258
+ // Arrange
259
+ var order = createValidOrder();
260
+ when(repository.save(any())).thenReturn(new Order("123"));
261
+
262
+ // Act
263
+ var result = service.processOrder(order);
264
+
265
+ // Assert
266
+ assertThat(result.getId()).isEqualTo("123");
267
+ verify(repository).save(order);
268
+ }
269
+
270
+ @Test
271
+ void processOrder_withInvalidOrder_throwsValidationException() {
272
+ // Arrange
273
+ var order = createInvalidOrder();
274
+
275
+ // Act & Assert
276
+ assertThatThrownBy(() -> service.processOrder(order))
277
+ .isInstanceOf(ValidationException.class)
278
+ .hasMessageContaining("items cannot be empty");
279
+ }
280
+
281
+ @ParameterizedTest
282
+ @CsvSource({"0, 100.0", "10, 90.0", "50, 50.0"})
283
+ void applyDiscount_calculatesCorrectly(int discount, double expected) {
284
+ assertThat(applyDiscount(100.0, discount)).isEqualTo(expected);
285
+ }
286
+ }
287
+ ```
288
+
289
+ ### File Conventions
290
+
291
+ - `src/test/java/` mirroring `src/main/java/` structure
292
+ - `*Test.java` suffix
293
+ - `mvn test` or `gradle test`
294
+
295
+ ## C# / .NET
296
+
297
+ **If the `grimoire:dotnet-unit-testing` skill is available, defer to it for full C#/.NET guidance.** It provides comprehensive xUnit, TUnit, Moq, and NSubstitute patterns.
298
+
299
+ ### Quick Reference (when dotnet skill is unavailable)
300
+
301
+ | Framework | When to Use |
302
+ |-----------|-------------|
303
+ | **xUnit** | Default, most universal |
304
+ | **NUnit** | If project already uses it |
305
+ | **MSTest** | Microsoft-first shops |
306
+
307
+ ```csharp
308
+ public class OrderServiceTests
309
+ {
310
+ private readonly Mock<IOrderRepository> _mockRepo;
311
+ private readonly OrderService _sut;
312
+
313
+ public OrderServiceTests()
314
+ {
315
+ _mockRepo = new Mock<IOrderRepository>();
316
+ _sut = new OrderService(_mockRepo.Object);
317
+ }
318
+
319
+ [Fact]
320
+ public async Task ProcessOrder_WithValidOrder_ReturnsId()
321
+ {
322
+ // Arrange
323
+ var order = CreateValidOrder();
324
+ _mockRepo.Setup(r => r.SaveAsync(It.IsAny<Order>()))
325
+ .ReturnsAsync(new Order { Id = "123" });
326
+
327
+ // Act
328
+ var result = await _sut.ProcessOrderAsync(order);
329
+
330
+ // Assert
331
+ Assert.Equal("123", result.Id);
332
+ }
333
+ }
334
+ ```
335
+
336
+ ### File Conventions
337
+
338
+ - `Tests/` or `*.Tests/` project mirroring source structure
339
+ - `*Tests.cs` suffix
340
+ - `dotnet test` to run
341
+
342
+ ## Ruby
343
+
344
+ ### Frameworks
345
+
346
+ | Framework | When to Use |
347
+ |-----------|-------------|
348
+ | **RSpec** | Default, most Ruby projects |
349
+ | **Minitest** | stdlib, Rails default |
350
+
351
+ ### RSpec Patterns
352
+
353
+ ```ruby
354
+ RSpec.describe OrderService do
355
+ let(:repository) { instance_double(OrderRepository) }
356
+ let(:service) { described_class.new(repository: repository) }
357
+
358
+ describe '#process_order' do
359
+ context 'with a valid order' do
360
+ it 'returns the order id' do
361
+ # Arrange
362
+ order = build(:order, :valid)
363
+ allow(repository).to receive(:save).and_return(Order.new(id: '123'))
364
+
365
+ # Act
366
+ result = service.process_order(order)
367
+
368
+ # Assert
369
+ expect(result.id).to eq('123')
370
+ expect(repository).to have_received(:save).with(order)
371
+ end
372
+ end
373
+
374
+ context 'with an invalid order' do
375
+ it 'raises ValidationError' do
376
+ order = build(:order, :invalid)
377
+ expect { service.process_order(order) }.to raise_error(ValidationError)
378
+ end
379
+ end
380
+ end
381
+ end
382
+ ```
383
+
384
+ ### File Conventions
385
+
386
+ - `spec/` directory mirroring `lib/` or `app/` structure
387
+ - `*_spec.rb` suffix
388
+ - `bundle exec rspec` to run
@@ -0,0 +1,135 @@
1
+ # TDD Workflow Patterns
2
+
3
+ Guidance on the test-driven development process, when to apply it, and advanced techniques.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Red-Green-Refactor](#red-green-refactor)
8
+ - [Transformation Priority Premise](#transformation-priority-premise)
9
+ - [When to Use TDD](#when-to-use-tdd)
10
+ - [When TDD Is Less Effective](#when-tdd-is-less-effective)
11
+ - [BDD and ATDD Extensions](#bdd-and-atdd-extensions)
12
+
13
+ ## Red-Green-Refactor
14
+
15
+ The core TDD cycle, repeated in small increments:
16
+
17
+ ### 1. Red — Write a Failing Test
18
+
19
+ Write the smallest test that describes the next piece of behavior. The test MUST fail before you write any production code. A test that passes immediately provides no confidence.
20
+
21
+ **Rules:**
22
+ - Write only ONE test at a time
23
+ - The test should compile/parse but fail at the assertion
24
+ - If the test passes immediately, it's either trivial or testing existing behavior
25
+
26
+ ### 2. Green — Make It Pass
27
+
28
+ Write the MINIMUM code to make the failing test pass. Do not add extra logic, handle cases not yet tested, or optimize.
29
+
30
+ **Rules:**
31
+ - Write the simplest code that makes the test pass
32
+ - It's OK to hardcode values initially — the next test will force generalization
33
+ - Do not add code for future tests
34
+ - All existing tests must still pass
35
+
36
+ ### 3. Refactor — Clean Up
37
+
38
+ With all tests green, improve the code structure without changing behavior. Tests give you the safety net.
39
+
40
+ **Rules:**
41
+ - No new functionality during refactoring
42
+ - All tests must remain green after each refactoring step
43
+ - Remove duplication, improve naming, extract methods
44
+ - Refactor both production code AND test code
45
+
46
+ ### Cycle Length
47
+
48
+ Each Red-Green-Refactor cycle should take 1-10 minutes. If you're spending more than 10 minutes in the Red or Green phase, the step is too large — break it down.
49
+
50
+ ## Transformation Priority Premise
51
+
52
+ Kent Beck's insight: when going from Red to Green, prefer simpler transformations over complex ones. Listed from simplest to most complex:
53
+
54
+ 1. **Constant** — return a hardcoded value
55
+ 2. **Scalar** — replace constant with a variable
56
+ 3. **Direct** — replace unconditional with conditional (if/else)
57
+ 4. **Collection** — operate on a collection instead of a scalar
58
+ 5. **Iteration** — add a loop
59
+ 6. **Recursion** — add recursive call
60
+ 7. **Assignment** — replace computed value with mutation
61
+
62
+ **Example — building FizzBuzz with TDD:**
63
+
64
+ ```
65
+ Test 1: input 1 → "1" Transformation: Constant
66
+ Test 2: input 2 → "2" Transformation: Scalar (use the input)
67
+ Test 3: input 3 → "Fizz" Transformation: Direct (add if)
68
+ Test 4: input 5 → "Buzz" Transformation: Direct (add another if)
69
+ Test 5: input 15 → "FizzBuzz" Transformation: Direct (add combined if)
70
+ Test 6: input 1-15 → full list Transformation: Iteration (generalize)
71
+ ```
72
+
73
+ By following this priority, you avoid over-engineering early and let the design emerge naturally from the tests.
74
+
75
+ ## When to Use TDD
76
+
77
+ TDD is most valuable when:
78
+
79
+ - **Business logic** — Complex rules, calculations, state machines. TDD forces you to think through all cases before implementing.
80
+ - **Algorithm development** — Sorting, parsing, validation, transformation logic. Tests serve as a specification.
81
+ - **Bug fixes** — Write a test that reproduces the bug first (Red), then fix it (Green). This prevents regressions.
82
+ - **API/interface design** — Writing tests first helps you design interfaces from the consumer's perspective.
83
+ - **Refactoring** — Ensure tests exist before refactoring. If they don't, write characterization tests first, then refactor.
84
+
85
+ ## When TDD Is Less Effective
86
+
87
+ TDD is not universally optimal. Use judgment:
88
+
89
+ - **UI/visual components** — Layout, styling, animations are hard to express as unit tests. Use visual regression testing or snapshot tests instead.
90
+ - **Exploratory/prototype code** — When you don't know what to build yet, writing tests first slows exploration. Spike first, then write tests.
91
+ - **Thin integration layers** — Simple pass-through code (e.g., a controller that calls a service) may not benefit from test-first approach. Integration tests are more valuable here.
92
+ - **Infrastructure/glue code** — Database migrations, config files, build scripts. Test these with integration or end-to-end tests.
93
+ - **External API wrappers** — Thin clients wrapping external APIs are better tested with integration tests against the real (or sandboxed) API.
94
+
95
+ For these cases, write tests AFTER the implementation (test-last), but still write them.
96
+
97
+ ## BDD and ATDD Extensions
98
+
99
+ ### Behavior-Driven Development (BDD)
100
+
101
+ BDD extends TDD by using natural language to describe behavior. Useful when tests need to be readable by non-developers.
102
+
103
+ **Given-When-Then** structure:
104
+
105
+ ```gherkin
106
+ Given a cart with items totaling $100
107
+ When a 10% discount is applied
108
+ Then the total should be $90
109
+ ```
110
+
111
+ Maps to test code:
112
+
113
+ ```python
114
+ def test_cart_with_10_percent_discount_totals_90():
115
+ # Given
116
+ cart = Cart(items=[Item(price=100)])
117
+
118
+ # When
119
+ cart.apply_discount(PercentageDiscount(10))
120
+
121
+ # Then
122
+ assert cart.total == 90.0
123
+ ```
124
+
125
+ ### Acceptance TDD (ATDD)
126
+
127
+ Write high-level acceptance tests before implementing a feature. These tests describe the feature from the user's perspective and drive the overall design. Unit tests (via TDD) then drive the implementation of each component.
128
+
129
+ **Flow:**
130
+ 1. Write acceptance test (fails — Red)
131
+ 2. Use TDD to implement components needed to pass it
132
+ 3. Acceptance test passes (Green)
133
+ 4. Refactor
134
+
135
+ ATDD is most valuable for features with clear acceptance criteria and when working with product owners or stakeholders.