From 71fbd5a6f30e2c7f6e5e49807cae18274bc44506 Mon Sep 17 00:00:00 2001 From: Dhemas Nurjaya 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 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 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 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 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 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 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 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 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 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 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 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:10–0: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 ` 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 # 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