1. 程式人生 > 其它 >《python程式設計從入門到實踐》讀書實踐筆記(二)

《python程式設計從入門到實踐》讀書實踐筆記(二)

本文是《python程式設計從入門到實踐》讀書實踐筆記11章的內容,主要包含測試,為體現測試的重要性,獨立成文。

11 測試程式碼

寫在前面的話,以下是我這些年開發中和測試相關的血淚史。

  • 對於一個bug,發現得越晚,處理它的成本就越高。
  • bug在一個複雜系統中時,找到它要比處理它麻煩的多。
  • 大多數bug都低階得令人髮指。
  • 永遠無法找到所有bug,成本和安全需要互相妥協,極端一般都不那麼美好。
  • 測試人員有他們的KPI,自己找的bug才適合自己

11.1函式自測、測試用例和unittest

Python標準庫unittest提供了程式碼測試工具,可以用於程式碼的單元測試。
如果要用unnitest,需要建一個unittest的類
比如,當前有一個函式,該函式被存在city_function.py中

def city_country(city, country, polulation=None):
    """根據輸入的城市,國家,人口,返回特定格式字串"""
    if polulation == None:
        city_country_rst = f"{city.title()}, {country.title()}"
    elif polulation is not None:
        city_country_rst = f"{city.title()}, {country.title()} - polulation {polulation}"
    return city_country_rst

再新建一個test_cities.py的檔案,以進行對剛才函式的測試

import unittest
from city_function import *

class CityFuncTestCase(unittest.TestCase):
    """測試city_function.py"""

    def test_city_country_only_func(self):
        """能夠正確地得到結果"""
        city_country_rst = city_country('shanghai', 'china')
        self.assertEqual(city_country_rst, 'Shanghai, China')

    def test_city_country_polulation_func(self):
        """能夠正確地得到結果"""
        city_country_rst = city_country('shanghai', 'china', 16_0000_0000)
        self.assertEqual(city_country_rst, 'Shanghai, China - polulation 1600000000')


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

對於CityfuncTestCase類,其中的子函式(每一個測試用例)必須以test_開頭,這樣才能在執行unitest.main()時自動呼叫這些測試用例。
如果一個函式的外部互動較少,可以在測試時適當減少用例。比如在這個city_country函式中,就沒有增加polulation數值範圍的校驗。

11.2 類自測

類測試和函式測試幾乎一樣,都是在unittest中新建一個類,來測試需要的方法或屬性

11.2.4 方法setUp()

可以通過在測試類(比如剛才的CityFuncTestCase)中,增加setUp()方法,來在每一個測試用例呼叫前,增加一些相同的操作。對應的tearDown()方法中的內容會在每一個測試用例呼叫後執行。

被測試的類,儲存在classEmployee.py中

class Employee():
    """記錄僱員的資訊"""
    def __init__(self, name, salary=2000):
        self.name = name
        self.salary = salary

    def give_raise(self, amount=500):
        self.salary += amount

測試指令碼test_classEmployee.py:

import unittest
from classEmployee import *

class testClassEmployee(unittest.TestCase):
    """針對Employee類的測試"""

    def setUp(self):
        """建立一個員工,用於測試"""
        self.default_emp = Employee('Zhangsan')
        self.sp_emp = Employee('Lisi', salary=5000)

    def test_default_new_employee(self):
        """測試預設薪水下的普通員工"""
        self.assertEqual(self.default_emp.salary, 2000)

    def test_sp_new_employee_with_salary_5000(self):
        """測試特定薪水下的新建員工"""
        self.assertEqual(self.sp_emp.salary, 5000)

    def test_employee_give_raise_default(self):
        """測試預設薪水下的普通員工"""
        self.default_emp.give_raise()
        self.assertEqual(self.default_emp.salary, 2500)

    def test_employee_give_raise_2000(self):
        """測試預設薪水下的普通員工"""
        self.default_emp.give_raise(amount=2000)
        self.assertEqual(self.default_emp.salary, 4000)


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

通過test_employee_give_raise_default()和test_employee_give_raise_2000()可以發現,單元測試的函式間是不會互相影響的。(這兩個函式都對self.default_emp進行了操作,但是這些操作並不互通)

unittest的其他資訊

最全面的肯定是python關於unittest的官方文件
其他一些blog也可以看起來,當然最主要的還是:

  1. 自測的願望
  2. 在實踐中不斷使用

後話

unittest只是python標準庫中的自測框架,其實還有一些框架可以選用。