用 FizzBuzz 練習 Red Green Refactor

記得我在第一次寫 Leetcode 的時候,寫完馬上按下 Submit,然後看哪個測試沒過就改到讓他過。

後來才知道這個感覺很接近測試驅動開發(Test-Driven Development,TDD) 的精神,差別是 TDD 是你自己先寫測試,不是等別人的測試來告訴你哪裡錯了。

TDD 的宗旨就是:

先寫測試,再寫程式。

有別於傳統先開發再測試的流程,TDD 有以下好處:

  1. 測試本身就是一種規格,撰寫的過程可以對規格有更深入的理解
  2. 如果規格有問題或是不明確,可以在寫測試的時候就發現
  3. 開發的過程就一邊在重構了,不用等到最後一刻才發現不敢改

而 Red-Green-Refactor 是 TDD 中一個重要的觀念。
透過 寫測試 -> fail (Red) -> 寫 Code (Green) -> 重構 的迭代流程讓程式碼變完整。

這個練習只需要兩個檔案

1
2
3
fizzbuzz-kata/
├── fizzbuzz.py ← 主程式
└── test_fizzbuzz.py ← 測試

Red-Green-Refactor 三步驟

步驟 檔案 做什麼
🔴 Red test_fizzbuzz.py 新增一個會失敗的測試,描述需求
🟢 Green fizzbuzz.py 最少的程式碼通過測試
🔵 Refactor fizzbuzz.py 整理程式碼,確認測試還是綠的

FizzBuzz Spec

在寫測試之前,必須先了解需求:

輸入 條件 輸出
任意數字 一般情況 數字字串,例如 1"1"
3 的倍數 n % 3 == 0 "Fizz"
5 的倍數 n % 5 == 0 "Buzz"
15 的倍數 n % 15 == 0 "FizzBuzz"

⚠️ TDD 之前就需要了解規格,才能把所有情況和邊界都納入測試。

TDD 練習過程

Cycle 1:fizzbuzz(1)"1"

🔴 Red — 加測試

1
2
3
# test_fizzbuzz.py
def test_1_returns_1():
assert fizzbuzz(1) == "1"
1
2
3
# fizzbuzz.py
def fizzbuzz(n):
pass

執行結果(失敗):

1
AssertionError: assert None == '1'

🟢 Green — 寫最少程式碼

1
2
3
# fizzbuzz.py
def fizzbuzz(n):
return "1"

執行結果:

1
1 passed

🔵 Refactor

是的你沒看錯,寫這樣也算通過測試可以進到下一個 cycle


Cycle 2:fizzbuzz(2)"2"

🔴 Red — 加測試

1
2
3
# test_fizzbuzz.py
def test_2_returns_2():
assert fizzbuzz(2) == "2"

執行結果(失敗):

1
AssertionError: assert '1' == '2'

🟢 Green — 寫最少程式碼

return "1" 只能處理 1,改成通用的字串轉換:

1
2
3
# fizzbuzz.py
def fizzbuzz(n):
return str(n)

執行結果:

1
2 passed

🔵 Refactor

只有一行不太需要重構


Cycle 3:fizzbuzz(3)"Fizz"

🔴 Red — 加測試

1
2
3
# test_fizzbuzz.py
def test_3_returns_fizz():
assert fizzbuzz(3) == "Fizz"

執行結果(失敗):

1
AssertionError: assert '3' == 'Fizz'

🟢 Green — 加入 3 的倍數判斷

1
2
3
4
5
# fizzbuzz.py
def fizzbuzz(n):
if n % 3 == 0:
return 'Fizz'
return str(n)

執行結果:

1
3 passed

🔵 Refactor

不用重構


Cycle 4:fizzbuzz(5)"Buzz"

🔴 Red — 加測試

1
2
3
# test_fizzbuzz.py
def test_5_returns_buzz():
assert fizzbuzz(5) == "Buzz"

執行結果(失敗):

1
AssertionError: assert '5' == 'Buzz'

🟢 Green — 加入 5 的倍數判斷

1
2
3
4
5
6
7
# fizzbuzz.py
def fizzbuzz(n):
if n % 3 == 0:
return 'Fizz'
elif n % 5 == 0:
return 'Buzz'
return str(n)

執行結果:

1
4 passed

🔵 Refactor

不用重構


Cycle 5:fizzbuzz(15)"FizzBuzz"

🔴 Red — 加測試

1
2
3
# test_fizzbuzz.py
def test_15_returns_fizzbuzz():
assert fizzbuzz(15) == "FizzBuzz"

執行結果(失敗):

1
AssertionError: assert 'Fizz' == 'FizzBuzz'

🟢 Green

1
2
3
4
5
6
7
8
9
# fizzbuzz.py
def fizzbuzz(n):
if n % 3 == 0 and n % 5 == 0:
return 'FizzBuzz'
elif n % 3 == 0:
return 'Fizz'
elif n % 5 == 0:
return 'Buzz'
return str(n)

執行結果:

1
5 passed

🔵 Refactor

這邊可以思考兩種寫法

n % 3 == 0 and n % 5 == 0 或是 n % 15 == 0

  • n % 3 == 0 and n % 5 == 0:意圖更清楚(同時是 3 和 5 的倍數)
  • n % 15 == 0:更簡潔

如果效能不會差很多的話通常我都是選易讀的選項


Cycle 6、7、8:驗證通用邏輯

加入更多測試案例,確認邏輯真的通用:

1
2
3
4
5
6
7
8
9
# test_fizzbuzz.py
def test_6_returns_fizz():
assert fizzbuzz(6) == "Fizz"

def test_10_returns_buzz():
assert fizzbuzz(10) == "Buzz"

def test_30_returns_fizzbuzz():
assert fizzbuzz(30) == "FizzBuzz"

執行結果:

1
8 passed

Final Code

1
2
3
4
5
6
7
8
9
# fizzbuzz.py
def fizzbuzz(n):
if n % 3 == 0 and n % 5 == 0:
return 'FizzBuzz'
elif n % 3 == 0:
return 'Fizz'
elif n % 5 == 0:
return 'Buzz'
return str(n)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# test_fizzbuzz.py
from fizzbuzz import fizzbuzz

class TestFizzBuzz:

def test_1_returns_1(self):
assert fizzbuzz(1) == "1"

def test_2_returns_2(self):
assert fizzbuzz(2) == "2"

def test_3_returns_fizz(self):
assert fizzbuzz(3) == "Fizz"

def test_5_returns_buzz(self):
assert fizzbuzz(5) == "Buzz"

def test_15_returns_fizzbuzz(self):
assert fizzbuzz(15) == "FizzBuzz"

def test_6_returns_fizz(self):
assert fizzbuzz(6) == "Fizz"

def test_10_returns_buzz(self):
assert fizzbuzz(10) == "Buzz"

def test_30_returns_fizzbuzz(self):
assert fizzbuzz(30) == "FizzBuzz"

心得

雖然之前就有讀過類似概念,但也只是看過去而已,這次搭配 AI 教練自己下來動手作很有感。

  1. 原來真的可以為了讓綠燈通過,寫出 return "1" 這種程式碼(雖然很快就會在下一輪被改掉)。
  2. 做起來真的和傳統 SDLC 不同,我的感覺是在寫測試階段,就會逼自己去了解規格,並思考各種邊界。
  3. 不確定這樣的開發方式會不會比傳統快,傳統的開發方式是大腦連貫的把已知邏輯順暢的寫完但不會管測試,而 TDD 則是片段式的慢慢把功能補齊,可以有預感 TDD 不會 overengineering。
  4. 拿 Leetcode 的題目練習感覺是個不錯的主意。

思考:

  1. 為什麼要用 Red-Green-Refactor 來執行 TDD,一次把所有想到的測試都寫完然後開發不好嗎?

用 FizzBuzz 練習 Red Green Refactor
https://weiblog.me/2026-05-12/practice-rdr-with-fizzbuzz/
Author
wei
Posted on
May 12, 2026
Licensed under