tdd-workshop/fizzbuzz/DEMO_SCRIPT.md
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

7.3 KiB
Raw Permalink Blame History

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:

// In test/fizzbuzz_test.dart
test('returns "1" for 1', () {
  expect(fizzBuzz(1), equals('1'));
});

Run dart testFAILS (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:

// In lib/fizzbuzz.dart
String fizzBuzz(int n) {
  return '1';
}

Run dart testPASSES (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:

test('returns "2" for 2', () {
  expect(fizzBuzz(2), equals('2'));
});

Run dart testFAILS (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:

String fizzBuzz(int n) {
  return n.toString();
}

Run dart testPASSES (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:

test('returns "Fizz" for 3', () {
  expect(fizzBuzz(3), equals('Fizz'));
});

Run dart testFAILS (Expected: 'Fizz', Actual: '3')

GREEN Phase:

What to Say:

Okay, now I need to check divisibility by 3.

What to Do:

String fizzBuzz(int n) {
  if (n % 3 == 0) return 'Fizz';
  return n.toString();
}

Run dart testPASSES (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:

test('returns "Buzz" for 5', () {
  expect(fizzBuzz(5), equals('Buzz'));
});

Run dart testFAILS (Expected: 'Buzz', Actual: '5')

GREEN Phase:

What to Do:

String fizzBuzz(int n) {
  if (n % 3 == 0) return 'Fizz';
  if (n % 5 == 0) return 'Buzz';
  return n.toString();
}

Run dart testPASSES (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:

test('returns "FizzBuzz" for 15', () {
  expect(fizzBuzz(15), equals('FizzBuzz'));
});

Run dart testFAILS (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:

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 testPASSES (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!"