iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0

第 26 天:Python 的測試驅動開發(Test-Driven Development, TDD)

課程目標:

今天將介紹如何在 Python 中實踐測試驅動開發(TDD)。透過 TDD,我們能在程式開發的初期就撰寫測試案例,以確保每個新功能都符合需求,並且通過測試。這種方法可以大幅提升程式的可靠性與可維護性。


1. 什麼是測試驅動開發(TDD)?

測試驅動開發是一種軟體開發流程,強調在撰寫功能代碼之前先撰寫測試。開發者首先撰寫會失敗的測試,然後實作能通過這些測試的代碼,最後進行代碼重構以確保最佳實踐。

TDD 流程:
  1. 撰寫測試: 根據需求撰寫測試案例。
  2. 執行測試: 測試應該失敗,因為功能尚未實作。
  3. 編寫程式碼: 實作功能,讓測試通過。
  4. 再次測試: 確保所有測試通過。
  5. 重構程式碼: 改善程式結構,保持測試通過。

2. unittest 模組介紹

unittest 是 Python 內建的單元測試框架,適合進行 TDD。它能夠自動執行測試並報告結果,並提供了豐富的測試工具。

簡單範例:
import unittest

def add(x, y):
    return x + y

class TestMath(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(2, 3), 5)
        self.assertEqual(add(-1, 1), 0)

if __name__ == '__main__':
    unittest.main()

這段程式展示了如何利用 unittest 來測試一個簡單的加法函數。


3. TDD 流程範例:撰寫階乘函數

我們將透過 TDD 的方法,開發一個階乘函數。首先撰寫測試案例,然後實作功能,再進行重構。

第一步:撰寫測試
import unittest

class TestMathFunctions(unittest.TestCase):
    def test_factorial(self):
        self.assertEqual(factorial(5), 120)

if __name__ == '__main__':
    unittest.main()

在這裡,我們還沒有實作 factorial 函數,測試將失敗。

第二步:實作 factorial 函數
def factorial(n):
    if n == 0:
        return 1
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

這個階段,我們實作了 factorial 函數,並再次執行測試,應該會通過。

第三步:代碼重構

檢查程式碼是否可以進行簡化或改進。例如,利用遞迴來簡化階乘函數:

def factorial(n):
    return 1 if n == 0 else n * factorial(n - 1)

4. setUptearDown 方法

在某些情況下,你可能需要在測試之前準備一些環境或資源(例如建立資料庫連線)。setUp 方法在每個測試之前執行,tearDown 方法則在測試後執行。

範例:
import unittest

class TestExample(unittest.TestCase):
    def setUp(self):
        print("執行測試之前的初始化")

    def tearDown(self):
        print("測試結束後的清理工作")

    def test_sample(self):
        self.assertEqual(1 + 1, 2)

if __name__ == '__main__':
    unittest.main()

5. 測試例外狀況

在某些情況下,你可能需要測試程式是否正確處理例外。例如,測試除以 0 時是否會引發錯誤。

範例:
import unittest

def divide(x, y):
    if y == 0:
        raise ValueError("不能除以 0")
    return x / y

class TestDivideFunction(unittest.TestCase):
    def test_divide_by_zero(self):
        with self.assertRaises(ValueError):
            divide(10, 0)

if __name__ == '__main__':
    unittest.main()

6. 實作練習

練習 1: 撰寫一個計算質數的函數,並使用 TDD 方法完成測試與實作。

練習 2: 為一個數學函數庫撰寫測試,包含加法、減法、乘法和除法的測試。


練習 1:撰寫一個計算質數的函數,並使用 TDD 方法完成測試與實作

Test-Driven Development (TDD) 是在編寫功能代碼之前,先撰寫測試用例的開發方式,這確保了功能代碼完全滿足測試要求。以下是實作計算質數的步驟。

步驟:
  1. 先撰寫測試用例,檢查是否正確判斷某個數是否為質數。
  2. 在測試用例基礎上,實作質數判斷函數。
  3. 運行測試並修正錯誤,直至所有測試通過。
範例代碼:
  1. 撰寫測試用例:
import unittest

# 測試用例
class TestPrimeFunction(unittest.TestCase):
    
    def test_prime_number(self):
        self.assertTrue(is_prime(7))  # 7 是質數
        
    def test_non_prime_number(self):
        self.assertFalse(is_prime(4))  # 4 不是質數
        
    def test_one_is_not_prime(self):
        self.assertFalse(is_prime(1))  # 1 不是質數
        
    def test_prime_large_number(self):
        self.assertTrue(is_prime(29))  # 29 是質數

# 運行單元測試
if __name__ == '__main__':
    unittest.main()
  1. 基於測試實作函數:
def is_prime(n):
    if n <= 1:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True
解釋:
  • is_prime 函數判斷一個數是否為質數。若數字小於或等於 1,返回 False。若能被 2 到該數的平方根之間的任何數整除,也不是質數。
  • 測試涵蓋了質數、非質數、特殊數字 1,以及較大的質數情況。
TDD 過程:
  1. 先撰寫測試用例,預期結果。
  2. 編寫 is_prime 函數,通過測試。
  3. 測試驗證代碼運行結果是否正確。

練習 2:為一個數學函數庫撰寫測試,包含加法、減法、乘法和除法的測試

這裡,我們會建立一個數學函數庫,並使用 unittest 為其撰寫單元測試。TDD 的流程同樣適用於這裡。

步驟:
  1. 先撰寫加法、減法、乘法、除法的測試。
  2. 編寫相應的數學函數,確保它們通過所有測試。
範例代碼:
  1. 撰寫測試用例:
import unittest

# 測試用例
class TestMathFunctions(unittest.TestCase):
    
    def test_add(self):
        self.assertEqual(add(3, 4), 7)
        
    def test_subtract(self):
        self.assertEqual(subtract(10, 5), 5)
        
    def test_multiply(self):
        self.assertEqual(multiply(3, 4), 12)
        
    def test_divide(self):
        self.assertEqual(divide(8, 2), 4)
        
    def test_divide_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            divide(10, 0)

# 運行單元測試
if __name__ == '__main__':
    unittest.main()
  1. 實作數學函數庫:
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

def multiply(a, b):
    return a * b

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("除數不能為 0")
    return a / b
解釋:
  • 測試分別檢查了加法、減法、乘法、除法,以及除零錯誤的情況。
  • 數學函數 addsubtractmultiplydivide 根據測試要求編寫。
  • divide 函數會在除數為 0 時拋出 ZeroDivisionError,並通過測試 assertRaises 來檢查此行為。
TDD 過程:
  1. 先撰寫測試用例,確保每個函數的預期行為明確。
  2. 實作對應的數學運算函數。
  3. 執行測試,修正函數,直到所有測試通過。

這樣的測試驅動開發 (TDD) 方法,能夠確保程式碼的正確性並便於後續維護。撰寫測試能防止潛在的邏輯錯誤,也有助於提升開發過程的品質。


7. 小結

今天的課程介紹了 TDD 的基本流程及 Python 的 unittest 模組。透過 TDD,你能在程式開發過程中更有條理地編寫代碼,並確保所有功能通過測試,這能幫助你更有效率地構建穩定的應用程式。


上一篇
跟著 ChatGPT成為程式大佬!Python 單元測試與自動化測試
下一篇
跟著 ChatGPT成為程式大佬!Python 資料庫操作與 SQLite
系列文
如果讓chatgpt參加iThome鐵人賽,他竟然寫出...!?31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言