iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
Software Development

軟體開發養成計畫:以小程式實作深化開發能力系列 第 28

[Day28]一週回顧 & 讓程式更有韌性:錯誤處理與測試

  • 分享至 

  • xImage
  •  

1.第四週回顧

這一週我總共寫了六篇文章:

第二十二天:讓時間動起來:Python 的 datetime 與 schedule 入門
第二十三天:用 Python 寫第九個小程式:打字遊戲
第二十四天:程式開發必備技能:Git & GitHub 的基礎應用
第二十五天:用 Python 寫第十個小程式:短網址生產器
第二十六天:Hash ≠ 加密,Base62 ≠ 魔法:資料處理入門基礎
第二十七天:用 Python 寫第十一個小程式:圖片壓縮工具

這六天的學習中,有三天偏重技術與觀念,另外三天則是小程式實作。
這週的學習內容橫跨時間控制、版本管理、資料編碼與多個小程式實作,讓我對「程式如何融入生活」有了更深的體會。透過 datetime 與 schedule,我學會讓程式與時間互動,實現自動提醒與排程的功能;在 Git 與 GitHub 的練習中,我初步掌握了版本控制的概念,理解團隊協作的基礎流程;而 Hash 與 Base62 的應用則讓我更明白資料處理的邏輯與轉換思維。除此之外,Typing Game、短網址工具與 PictureCompressor 這三個小程式的完成,讓我體會到從想法到作品的過程不僅是技術的累積,更是創意與實用性的結合,第四週的主題就像是在讓程式真正走入日常,從時間管理、資料壓縮到學習輔助,每個小功能都在提醒我——寫程式不只是解題,而是讓生活更有效率、更有趣的一種方式

如果前三週的學習主題分別是「介面友善」、「專業化」與「程式走向世界」,那麼第四週我覺得核心就是「把程式能力落實到生活應用中」。透過小程式實作,我不僅練習了 Python 技巧,也開始理解如何把抽象概念變成實際可用的工具。因此,我想將我的小程式加上測試與錯誤處理功能,讓在預防程式崩潰的同時,也能在使用者犯錯時給出友善提示。

例如說這次我打算先讓我的短網址生產器加上測試與錯誤處理功能作為示範,接下來我們就一起來整理程式吧!

2.Error Handling錯誤處理分析

(1)使用方式

在這份程式中,錯誤處理主要透過 try-except 區塊來避免程式在發生例外狀況時直接崩潰,例如讀寫檔案、解析 JSON、或使用者輸入錯誤。

(2)範例程式碼

範例一:

def load_data():
    global url_map
    if os.path.exists(DATA_FILE):
        try:
            with open(DATA_FILE, "r", encoding="utf-8") as f:
                url_map = json.load(f)
        except (json.JSONDecodeError, OSError):
            print("⚠️ Error loading data, resetting file...")
            url_map = {}
    else:
        url_map = {}
  • try 嘗試開啟與讀取 JSON 檔案。
  • except (json.JSONDecodeError, OSError) 捕捉可能出現的錯誤:若檔案損壞或格式錯誤,不會讓程式停止,而是重設為空資料,並顯示警告訊息提醒使用者。

範例二在主程式中:

try:
    choice = input("Select an option: ")
    if choice == "1":
        long_url = input("Enter URL: ").strip()
        short = shorten_url(long_url)
        print(f"✅ Shortened: {short}")
    elif choice == "2":
        short = input("Enter short code: ").strip()
        print(f"🔗 Original: {get_original(short)}")
    else:
        print("❌ Invalid choice.")
except Exception as e:
    print(f"⚠️ Unexpected error: {e}")
  • 不論使用者輸入錯誤或程式執行中出現意外錯誤,都不會整個中斷。
  • 可維持流暢操作體驗。

(3)優點分析:

  • 即使發生錯誤也不會崩潰。
  • 印出具體錯誤訊息,有助於了解問題來源。
  • 避免突如其來的中斷與錯誤畫面。
  • 防止錯誤資料導致崩潰或覆蓋資料。

3.Testing Functionality測試功能分析

(1)使用方式

為了確保各功能模組能正確運作,加入了 unittest 測試架構
這能讓我們自動化檢查「網址縮短」、「查詢原始網址」與「資料載入/儲存」是否都正常。

(2)範例程式碼

範例一:

import unittest

class TestURLShortener(unittest.TestCase):
    def test_shorten_and_retrieve(self):
        url = "https://example.com"
        short = shorten_url(url)
        self.assertEqual(get_original(short), url)

    def test_nonexistent_code(self):
        self.assertIsNone(get_original("xxxxxx"))

if __name__ == "__main__":
    unittest.main()
  • 使用 unittest.TestCase 建立測試類別。
  • 透過 assertEqual() 與 assertIsNone() 來驗證程式邏輯。
  • 若任一測試失敗,Python 會回傳錯誤報告,方便除錯。

(3)優點分析

  • 不用手動輸入與驗證結果。
  • 之後修改程式也能確保核心功能仍正確。
  • 經過測試的功能更值得信任。
  • 測試能立刻顯示錯誤位置,加快修正速度。

4.短網址生產器升級版介紹

這裡是加入測試與錯誤處理功能的短網址生產器程式碼,有興趣的人可以看[ Day25 ] 小小短碼,大大學習:用 Python 打造屬於自己的短網址工具作為參照比較。

(1)資料處理模組

這部分負責載入、儲存、維護短網址資料,url_data.json確保使用者關閉程式後資料仍能保存,並以錯誤處理機制防止檔案讀寫出錯。

DATA_FILE = "url_data.json"
url_map = {}

def load_data():
    global url_map
    if os.path.exists(DATA_FILE):
        try:
            with open(DATA_FILE, "r", encoding="utf-8") as f:
                url_map = json.load(f)
                if not isinstance(url_map, dict):
                    url_map = {}
        except (json.JSONDecodeError, OSError):
            messagebox.showwarning("警告", "資料檔案損壞或格式錯誤,已重置。")
            url_map = {}
    else:
        url_map = {}

def save_data():
    try:
        with open(DATA_FILE, "w", encoding="utf-8") as f:
            json.dump(url_map, f, indent=4, ensure_ascii=False)
    except Exception as e:
        messagebox.showerror("錯誤", f"儲存失敗:{e}")
  • load_data():讀取 JSON 檔案,若不存在則建立空字典。
  • save_data():儲存目前短網址對應表到檔案中。
  • 錯誤處理:若 JSON 檔案損壞,會自動重置並提醒使用者。

(2)短網址邏輯模組

整個應用最重要的部分,負責產生、查詢、更新短網址邏輯,確保短碼唯一且統計次數正確。

def generate_short_code(length=6):
    characters = string.ascii_letters + string.digits
    return ''.join(random.choice(characters) for _ in range(length))

def shorten_url(long_url):
    if not (long_url.startswith("http://") or long_url.startswith("https://")):
        return None
    short_code = generate_short_code()
    while short_code in url_map:
        short_code = generate_short_code()
    url_map[short_code] = {"url": long_url, "count": 0}
    save_data()
    return f"http://short.ly/{short_code}"

def open_short_url(short_code):
    if short_code in url_map:
        url_map[short_code]["count"] += 1
        save_data()
        return url_map[short_code]["url"], url_map[short_code]["count"]
    else:
        return None, None
  • generate_short_code():產生隨機英數組合的短。
  • shorten_url():建立短網址並保存。
  • open_short_url():點擊後開啟原始網址並累加點擊次數。
  • delete_short_url():刪除短碼對應資料。

(3)假資料生成與測試模組

def generate_dummy_data(n=5, force=False):
    if not force and url_map:
        messagebox.showinfo("提示", "目前已有資料,若要強制新增請使用強制功能。")
        return

    sample_hosts = [
        "https://example.com",
        "https://github.com",
        "https://docs.python.org",
        "https://www.wikipedia.org",
        "https://www.youtube.com"
    ]
    added = 0
    while added < n:
        long_url = random.choice(sample_hosts)
        short_code = generate_short_code()
        if short_code in url_map:
            continue
        url_map[short_code] = {"url": long_url, "count": random.randint(0, 50)}
        added += 1

    save_data()
    refresh_listbox()
    messagebox.showinfo("完成", f"已新增 {added} 筆假資料。")

def run_tests():
    url_map.clear()
    save_data()
    print("=== 測試開始 ===")
    try:
        url = "https://www.google.com"
        short = shorten_url(url)
        assert short and short.startswith("http://short.ly/")
        print(f"生成短網址測試通過: {short}")
        print("=== 測試結束,所有測試通過 ===")
        messagebox.showinfo("測試完成", "所有測試已通過 (詳細請看控制台)")
    except AssertionError:
        messagebox.showerror("測試失敗", "有測試未通過,請查看控制台輸出。")
  • 假資料生成 (generate_dummy_data):自動建立測試用短網址。
  • 單元測試 (run_tests):模擬功能測試,確保主要功能正確運作。

(4)GUI 視覺介面模組

root = tk.Tk()
root.title("短網址管理器")
root.geometry("700x540")
root.configure(bg="#f0f4f7")

title_label = tk.Label(root, text="短網址管理器", font=("Microsoft YaHei", 18, "bold"), bg="#f0f4f7", fg="#333")
title_label.pack(pady=10)

listbox = tk.Listbox(root, width=95, height=16, font=("Consolas", 10))
listbox.pack(pady=5)

frame_buttons = tk.Frame(root, bg="#f0f4f7")
frame_buttons.pack(side=tk.BOTTOM, pady=15)

btn_generate = tk.Button(frame_buttons, text="生成短網址", command=generate_url, bg="#2196f3", fg="white", font=("Microsoft YaHei", 11, "bold"), width=15)
btn_generate.grid(row=0, column=0, padx=6)

btn_test = tk.Button(frame_buttons, text="執行測試", command=run_tests, bg="#9c27b0", fg="white", font=("Microsoft YaHei", 11, "bold"), width=15)
btn_test.grid(row=0, column=3, padx=6)

btn_dummy = tk.Button(frame_buttons, text="生成假資料(5筆)", command=lambda: generate_dummy_data(5, force=True), bg="#607d8b", fg="white", font=("Microsoft YaHei", 11, "bold"), width=18)
btn_dummy.grid(row=1, column=1, pady=10)
  • 增加了新介面,讓它更整潔且顏色區分明確(藍:新增、橘:開啟、紅:刪除、紫:測試、灰:假資料)。

(5)初始化與自動行為模組

啟動程式時自動執行:1.載入資料。2.若資料為空,自動生成假資料。3.最後啟動主迴圈。

load_data()
# 若資料是空的,自動產生 5 筆假資料
if not url_map:
    generate_dummy_data(5, force=True)
else:
    refresh_listbox()

root.mainloop()

5.展示結果

呈現五筆假資料來測試:
https://ithelp.ithome.com.tw/upload/images/20251004/20178527ky7Q26UIOM.jpg
測試結果:
https://ithelp.ithome.com.tw/upload/images/20251004/20178527Vf8wZltebx.jpg


延續前幾天的實作,我今天想讓我的短網址工具更進一步——不只是能「動起來」,而是能「好好地動」。
於是,我加入了錯誤處理與測試程式的功能。
一開始看到那些 try-except、unittest 的語法時,其實有點怕會把整個程式搞亂。
但當我慢慢測試、觀察結果時,心裡有一種特別的成就感:
原本會當掉的地方,現在變成會彈出提示訊息;原本要一一點開檢查的功能,現在能靠測試自動驗證。
這次的學習讓我真的感受到,程式不只是冰冷的程式碼,而是一段段思考與溫度的累積。
我開始懂得如何讓自己的作品更穩定、更體貼,也更接近我理想中那個「能陪伴使用者」的智慧工具。
未來我會繼續挑戰更多進階應用,讓每一次的改版,都成為我前進的足跡~


上一篇
[Day27]Python 圖片壓縮技巧:PictureCompressor
下一篇
[Day29]AI 與自動化測試的未來:TDD 新思維下的軟體品質革命
系列文
軟體開發養成計畫:以小程式實作深化開發能力30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言