- 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
346 lines
7.3 KiB
Markdown
346 lines
7.3 KiB
Markdown
# 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 <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!"
|
||
```
|