tdd-workshop/docs/fizzbuzz-git-history.patch
fiatcode c3355063f2 Initial commit: Complete TDD workshop materials
- Workshop documentation (WORKSHOP_PLAN, FACILITATOR_GUIDE, etc.)
- FizzBuzz kata with demo script (git history to be recreated)
- Password Validator kata with demo script and solution
- Shopping Cart kata with demo script and solution
- Setup guide and TDD reference card for participants
2026-03-10 15:32:21 +07:00

733 lines
18 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 71fbd5a6f30e2c7f6e5e49807cae18274bc44506 Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:44:55 +0700
Subject: [PATCH 01/12] Initial setup: Create FizzBuzz kata structure
---
lib/fizzbuzz.dart | 1 +
pubspec.yaml | 9 +++++++++
test/fizzbuzz_test.dart | 6 ++++++
3 files changed, 16 insertions(+)
create mode 100644 lib/fizzbuzz.dart
create mode 100644 pubspec.yaml
create mode 100644 test/fizzbuzz_test.dart
diff --git a/lib/fizzbuzz.dart b/lib/fizzbuzz.dart
new file mode 100644
index 0000000..8c2215b
--- /dev/null
+++ b/lib/fizzbuzz.dart
@@ -0,0 +1 @@
+// FizzBuzz kata - empty implementation file
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..5e6dee3
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,9 @@
+name: fizzbuzz
+description: FizzBuzz kata for TDD workshop live demonstration
+version: 1.0.0
+
+environment:
+ sdk: ^3.0.0
+
+dev_dependencies:
+ test: ^1.24.0
diff --git a/test/fizzbuzz_test.dart b/test/fizzbuzz_test.dart
new file mode 100644
index 0000000..efeefe0
--- /dev/null
+++ b/test/fizzbuzz_test.dart
@@ -0,0 +1,6 @@
+import 'package:test/test.dart';
+import '../lib/fizzbuzz.dart';
+
+void main() {
+ // Tests will be added step by step during the demo
+}
--
2.53.0
From 39357bebb6432b14ae45942da92de4804e747d02 Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:45:29 +0700
Subject: [PATCH 02/12] RED: Add test for input 1 returning "1"
---
test/fizzbuzz_test.dart | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/test/fizzbuzz_test.dart b/test/fizzbuzz_test.dart
index efeefe0..27e4266 100644
--- a/test/fizzbuzz_test.dart
+++ b/test/fizzbuzz_test.dart
@@ -2,5 +2,7 @@ import 'package:test/test.dart';
import '../lib/fizzbuzz.dart';
void main() {
- // Tests will be added step by step during the demo
+ test('returns "1" for 1', () {
+ expect(fizzBuzz(1), equals('1'));
+ });
}
--
2.53.0
From 334c5b510611a36ccd7b5db02b3a7ad1e3ae0863 Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:45:41 +0700
Subject: [PATCH 03/12] GREEN: Return "1" (hardcoded minimal solution)
---
lib/fizzbuzz.dart | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/lib/fizzbuzz.dart b/lib/fizzbuzz.dart
index 8c2215b..0178355 100644
--- a/lib/fizzbuzz.dart
+++ b/lib/fizzbuzz.dart
@@ -1 +1,5 @@
-// FizzBuzz kata - empty implementation file
+// FizzBuzz kata - TDD progression
+
+String fizzBuzz(int n) {
+ return '1';
+}
--
2.53.0
From e0ae220dea66522d1726e342820f7e2d61edaafb Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:45:51 +0700
Subject: [PATCH 04/12] RED: Add test for input 2 returning "2"
---
test/fizzbuzz_test.dart | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/fizzbuzz_test.dart b/test/fizzbuzz_test.dart
index 27e4266..7b517ae 100644
--- a/test/fizzbuzz_test.dart
+++ b/test/fizzbuzz_test.dart
@@ -5,4 +5,8 @@ void main() {
test('returns "1" for 1', () {
expect(fizzBuzz(1), equals('1'));
});
+
+ test('returns "2" for 2', () {
+ expect(fizzBuzz(2), equals('2'));
+ });
}
--
2.53.0
From 4c0471c02ac4bfef56517044519c65478ecd3dbd Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:46:03 +0700
Subject: [PATCH 05/12] GREEN: Return string representation of number
---
lib/fizzbuzz.dart | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/fizzbuzz.dart b/lib/fizzbuzz.dart
index 0178355..4da1cfa 100644
--- a/lib/fizzbuzz.dart
+++ b/lib/fizzbuzz.dart
@@ -1,5 +1,5 @@
// FizzBuzz kata - TDD progression
String fizzBuzz(int n) {
- return '1';
+ return n.toString();
}
--
2.53.0
From 919e90fd474a2cc4200c9d6d0a0235365f6b718c Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:46:14 +0700
Subject: [PATCH 06/12] RED: Add test for input 3 returning "Fizz"
---
test/fizzbuzz_test.dart | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/fizzbuzz_test.dart b/test/fizzbuzz_test.dart
index 7b517ae..27b1ce6 100644
--- a/test/fizzbuzz_test.dart
+++ b/test/fizzbuzz_test.dart
@@ -9,4 +9,8 @@ void main() {
test('returns "2" for 2', () {
expect(fizzBuzz(2), equals('2'));
});
+
+ test('returns "Fizz" for 3', () {
+ expect(fizzBuzz(3), equals('Fizz'));
+ });
}
--
2.53.0
From 1db35633b102b359860a7e85361d733038e8655d Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:46:27 +0700
Subject: [PATCH 07/12] GREEN: Return "Fizz" for numbers divisible by 3
---
lib/fizzbuzz.dart | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/fizzbuzz.dart b/lib/fizzbuzz.dart
index 4da1cfa..21150ec 100644
--- a/lib/fizzbuzz.dart
+++ b/lib/fizzbuzz.dart
@@ -1,5 +1,6 @@
// FizzBuzz kata - TDD progression
String fizzBuzz(int n) {
+ if (n % 3 == 0) return 'Fizz';
return n.toString();
}
--
2.53.0
From 08cc354e1f1798f062e9c947b7d64637870d8964 Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:46:38 +0700
Subject: [PATCH 08/12] RED: Add test for input 5 returning "Buzz"
---
test/fizzbuzz_test.dart | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/fizzbuzz_test.dart b/test/fizzbuzz_test.dart
index 27b1ce6..46f279a 100644
--- a/test/fizzbuzz_test.dart
+++ b/test/fizzbuzz_test.dart
@@ -13,4 +13,8 @@ void main() {
test('returns "Fizz" for 3', () {
expect(fizzBuzz(3), equals('Fizz'));
});
+
+ test('returns "Buzz" for 5', () {
+ expect(fizzBuzz(5), equals('Buzz'));
+ });
}
--
2.53.0
From 74d6e23f3faa7c7138178ce00c784c51e97677e4 Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:46:52 +0700
Subject: [PATCH 09/12] GREEN: Return "Buzz" for numbers divisible by 5
---
lib/fizzbuzz.dart | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/fizzbuzz.dart b/lib/fizzbuzz.dart
index 21150ec..063e7bd 100644
--- a/lib/fizzbuzz.dart
+++ b/lib/fizzbuzz.dart
@@ -2,5 +2,6 @@
String fizzBuzz(int n) {
if (n % 3 == 0) return 'Fizz';
+ if (n % 5 == 0) return 'Buzz';
return n.toString();
}
--
2.53.0
From ca23fe7f17e7d29d8e375a4a2fc1ac32bf8452c6 Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:47:04 +0700
Subject: [PATCH 10/12] RED: Add test for input 15 returning "FizzBuzz"
---
test/fizzbuzz_test.dart | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/fizzbuzz_test.dart b/test/fizzbuzz_test.dart
index 46f279a..f3203e3 100644
--- a/test/fizzbuzz_test.dart
+++ b/test/fizzbuzz_test.dart
@@ -17,4 +17,8 @@ void main() {
test('returns "Buzz" for 5', () {
expect(fizzBuzz(5), equals('Buzz'));
});
+
+ test('returns "FizzBuzz" for 15', () {
+ expect(fizzBuzz(15), equals('FizzBuzz'));
+ });
}
--
2.53.0
From f15c0989635612226d40e28c8507276ecd1c976d Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:47:17 +0700
Subject: [PATCH 11/12] GREEN: Return "FizzBuzz" for numbers divisible by both
3 and 5 (check 15 first!)
---
lib/fizzbuzz.dart | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/fizzbuzz.dart b/lib/fizzbuzz.dart
index 063e7bd..b312695 100644
--- a/lib/fizzbuzz.dart
+++ b/lib/fizzbuzz.dart
@@ -1,6 +1,7 @@
// FizzBuzz kata - TDD progression
String fizzBuzz(int n) {
+ if (n % 15 == 0) return 'FizzBuzz';
if (n % 3 == 0) return 'Fizz';
if (n % 5 == 0) return 'Buzz';
return n.toString();
--
2.53.0
From cd13284513f428908ec0035f0848a12c27636eaa Mon Sep 17 00:00:00 2001
From: Dhemas Nurjaya <dhemasnurjaya@outlook.com>
Date: Tue, 10 Mar 2026 11:48:30 +0700
Subject: [PATCH 12/12] Add documentation for FizzBuzz demo kata
---
DEMO_SCRIPT.md | 346 +++++++++++++++++++++++++++++++++++++++++++++++++
README.md | 56 ++++++++
2 files changed, 402 insertions(+)
create mode 100644 DEMO_SCRIPT.md
create mode 100644 README.md
diff --git a/DEMO_SCRIPT.md b/DEMO_SCRIPT.md
new file mode 100644
index 0000000..d0853e0
--- /dev/null
+++ b/DEMO_SCRIPT.md
@@ -0,0 +1,346 @@
+# FizzBuzz Live Demo Script
+
+**Duration:** 15 minutes (0:100:25 in workshop schedule)
+**Purpose:** Demonstrate RED-GREEN-REFACTOR cycle in real-time
+
+---
+
+## Setup (Before Demo Starts)
+
+- [ ] Open two windows side-by-side: `lib/fizzbuzz.dart` and `test/fizzbuzz_test.dart`
+- [ ] Have terminal ready at bottom with `dart test --watch` (optional) or be ready to run `dart test` manually
+- [ ] Start with the final completed kata, then `git reset --hard <initial-commit>` to beginning
+- [ ] Have git log open in another terminal: `git log --oneline --all`
+
+---
+
+## Demo Flow
+
+### Minutes 0-2: Introduction
+
+**What to Say:**
+```
+We're going to build FizzBuzz together using TDD. Everyone knows the rules, right?
+[Pause for nods]
+
+Divisible by 3 → "Fizz"
+Divisible by 5 → "Buzz"
+Divisible by both → "FizzBuzz"
+Otherwise → the number as a string
+
+Here's the key: I'm NOT going to design this first. I'm going to let the tests tell me what to code.
+
+Watch the rhythm:
+1. RED - Write a test, watch it fail
+2. GREEN - Write minimal code to pass
+3. REFACTOR - Clean up if needed
+
+Let's start.
+```
+
+**What to Do:**
+- Show the empty files
+- Run `dart test` → shows no tests or all passing (from setup)
+
+---
+
+### Minutes 2-4: Cycle 1 (Input 1 → "1")
+
+**RED Phase:**
+
+**What to Say:**
+```
+What's the simplest test I could write? Let me start with input 1 returning "1".
+```
+
+**What to Do:**
+```dart
+// In test/fizzbuzz_test.dart
+test('returns "1" for 1', () {
+ expect(fizzBuzz(1), equals('1'));
+});
+```
+
+Run `dart test` → **FAILS** (function doesn't exist)
+
+**What to Say:**
+```
+Good! It's RED. The test is failing for the right reason - the function doesn't exist yet.
+```
+
+**GREEN Phase:**
+
+**What to Say:**
+```
+Now, what's the minimum code to make this pass?
+[Pause, let them think]
+I could return the number... but even simpler: I'll just return "1".
+This looks silly, but watch what happens next.
+```
+
+**What to Do:**
+```dart
+// In lib/fizzbuzz.dart
+String fizzBuzz(int n) {
+ return '1';
+}
+```
+
+Run `dart test` → **PASSES** (GREEN ✅)
+
+**What to Say:**
+```
+GREEN! It passes. Now I could refactor, but there's nothing to clean up yet.
+Let's add another test.
+```
+
+**Time Check:** You should be at ~4 minutes
+
+---
+
+### Minutes 4-6: Cycle 2 (Input 2 → "2")
+
+**RED Phase:**
+
+**What to Say:**
+```
+Next simplest test: input 2 should return "2".
+```
+
+**What to Do:**
+```dart
+test('returns "2" for 2', () {
+ expect(fizzBuzz(2), equals('2'));
+});
+```
+
+Run `dart test` → **FAILS** (Expected: '2', Actual: '1')
+
+**What to Say:**
+```
+RED again. Now I have duplication in my tests - both expect numbers as strings.
+That's okay. Let me make it GREEN.
+```
+
+**GREEN Phase:**
+
+**What to Say:**
+```
+Now the hardcoded "1" won't work. What's the simplest solution?
+[Pause]
+Return the number as a string!
+```
+
+**What to Do:**
+```dart
+String fizzBuzz(int n) {
+ return n.toString();
+}
+```
+
+Run `dart test` → **PASSES** (both tests GREEN ✅)
+
+**What to Say:**
+```
+Both tests pass! Notice how the second test forced me to generalize.
+I didn't plan that - the tests guided me.
+```
+
+**Time Check:** You should be at ~6 minutes
+
+---
+
+### Minutes 6-9: Cycle 3 (Input 3 → "Fizz")
+
+**RED Phase:**
+
+**What to Say:**
+```
+Now it gets interesting. Input 3 should return "Fizz".
+```
+
+**What to Do:**
+```dart
+test('returns "Fizz" for 3', () {
+ expect(fizzBuzz(3), equals('Fizz'));
+});
+```
+
+Run `dart test` → **FAILS** (Expected: 'Fizz', Actual: '3')
+
+**GREEN Phase:**
+
+**What to Say:**
+```
+Okay, now I need to check divisibility by 3.
+```
+
+**What to Do:**
+```dart
+String fizzBuzz(int n) {
+ if (n % 3 == 0) return 'Fizz';
+ return n.toString();
+}
+```
+
+Run `dart test` → **PASSES** (all 3 tests GREEN ✅)
+
+**What to Say:**
+```
+All three pass! The algorithm is starting to emerge.
+```
+
+**Time Check:** You should be at ~9 minutes
+
+---
+
+### Minutes 9-11: Cycle 4 (Input 5 → "Buzz")
+
+**RED Phase:**
+
+**What to Say:**
+```
+Pattern should be clear now. Input 5 returns "Buzz".
+```
+
+**What to Do:**
+```dart
+test('returns "Buzz" for 5', () {
+ expect(fizzBuzz(5), equals('Buzz'));
+});
+```
+
+Run `dart test` → **FAILS** (Expected: 'Buzz', Actual: '5')
+
+**GREEN Phase:**
+
+**What to Do:**
+```dart
+String fizzBuzz(int n) {
+ if (n % 3 == 0) return 'Fizz';
+ if (n % 5 == 0) return 'Buzz';
+ return n.toString();
+}
+```
+
+Run `dart test` → **PASSES** (all 4 tests GREEN ✅)
+
+**What to Say:**
+```
+Four tests passing. But we haven't handled the tricky case yet...
+```
+
+---
+
+### Minutes 11-14: Cycle 5 (Input 15 → "FizzBuzz")
+
+**RED Phase:**
+
+**What to Say:**
+```
+Here's where it gets fun. What should 15 return?
+[Let them answer: "FizzBuzz"]
+Right! It's divisible by both 3 AND 5.
+```
+
+**What to Do:**
+```dart
+test('returns "FizzBuzz" for 15', () {
+ expect(fizzBuzz(15), equals('FizzBuzz'));
+});
+```
+
+Run `dart test` → **FAILS** (Expected: 'FizzBuzz', Actual: 'Fizz')
+
+**What to Say:**
+```
+Uh oh! It returns "Fizz" because 15 is divisible by 3, and we check that first.
+We need to check for BOTH before checking for either individually.
+```
+
+**GREEN Phase:**
+
+**What to Do:**
+```dart
+String fizzBuzz(int n) {
+ if (n % 15 == 0) return 'FizzBuzz'; // Or: if (n % 3 == 0 && n % 5 == 0)
+ if (n % 3 == 0) return 'Fizz';
+ if (n % 5 == 0) return 'Buzz';
+ return n.toString();
+}
+```
+
+Run `dart test` → **PASSES** (all 5 tests GREEN ✅)
+
+**What to Say:**
+```
+Perfect! All tests pass. ORDER MATTERS. That's a lesson the test taught us.
+```
+
+**Time Check:** You should be at ~14 minutes
+
+---
+
+### Minutes 14-15: Wrap Up
+
+**What to Say:**
+```
+Let me show you something cool.
+[Run: git log --oneline]
+
+See these commits? Each one is a RED or GREEN step. I can jump back to any point.
+
+Notice what just happened:
+1. I never wrote a "design document" for FizzBuzz
+2. The tests told me what code to write
+3. The algorithm emerged naturally from simple cases to complex
+4. When I made a mistake (checking 3 before 15), the test caught it immediately
+
+This is TDD. It feels weird at first, but it becomes automatic.
+
+Now YOU'RE going to try it. Pick your kata and start with the first test.
+```
+
+---
+
+## Tips for Smooth Delivery
+
+### DO:
+- **Narrate your thinking**: "What's the simplest test?" "What's the minimum code?"
+- **Pause before answering**: Let them think for 2-3 seconds
+- **Show RED clearly**: Let the test fail, read the error message aloud
+- **Type slowly**: They're watching and learning the syntax too
+- **Run tests frequently**: After every change
+
+### DON'T:
+- Rush through the cycles - the rhythm is what they're learning
+- Skip the RED step - it proves the test can fail
+- Write all tests at once - that's not TDD
+- Apologize for "simple" code - minimal solutions are the point
+- Skip explaining the 15/3/5 order issue - that's a key insight
+
+### If You Make a Typo:
+**Good!** Say: "Oops, typo. See how the test catches it?" Then fix it.
+
+### If Someone Asks a Question:
+Pause the demo, answer briefly, then continue. You have buffer time.
+
+---
+
+## Troubleshooting
+
+| Issue | Fix |
+|-------|-----|
+| Tests won't run | Check `dart pub get` was run |
+| Can't see failure messages clearly | Increase terminal font size |
+| Running behind schedule | Skip the "wrap up" or shorten it |
+| Running ahead | Add a 6th test (e.g., input 6 → "Fizz") |
+
+---
+
+## After the Demo
+
+Transition to hands-on:
+```
+"Alright, now you try it. Open one of the katas, uncomment the first test, and make it RED. Then make it GREEN. I'll be walking around - grab me if you get stuck. Go!"
+```
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c4ae816
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+# FizzBuzz Kata - Live Demo
+
+## Purpose
+
+This kata is designed for **live demonstration** during the TDD workshop introduction. It shows the RED-GREEN-REFACTOR cycle in a familiar problem that requires minimal explanation.
+
+## The Rules
+
+Write a function that converts numbers to strings according to these rules:
+
+- Numbers divisible by 3 → `"Fizz"`
+- Numbers divisible by 5 → `"Buzz"`
+- Numbers divisible by both 3 and 5 → `"FizzBuzz"`
+- All other numbers → the number as a string (e.g., `"1"`, `"2"`)
+
+## Git History as Teaching Tool
+
+This kata is built with **12 commits** showing each step of the TDD cycle:
+
+```bash
+git log --oneline --all
+```
+
+Each commit shows either:
+- **RED**: A failing test
+- **GREEN**: Minimal code to pass
+- **REFACTOR**: Code cleanup (if needed)
+
+## Using This for the Demo
+
+See `DEMO_SCRIPT.md` for detailed talking points and timing guide.
+
+## Running the Tests
+
+```bash
+dart pub get
+dart test
+```
+
+## Checking Out Individual Steps
+
+To see the code at any point:
+
+```bash
+git log --oneline # See all commits
+git checkout <commit-hash> # Jump to that step
+git checkout main # Return to final solution
+```
+
+## The "Aha!" Moment
+
+Notice how the simple tests (1, 2) led to a general solution (n.toString()), and the Fizz/Buzz tests forced the algorithm to emerge naturally. **We never designed the whole solution upfront—the tests revealed it step by step.**
+
+## For Facilitators
+
+This is your warm-up kata. Practice it a few times before the workshop so you can live-code it smoothly while talking through your thinking process.
--
2.53.0