python《從入門到實踐》第11章測試程式碼 筆記
測試是用來確定程式碼在面對各種輸入時能按照要求進行工作,提前測試有利於防止新程式碼不會破壞程式既有的行為,本章使用unittest中的工具,編寫測試用例,核實輸入將得到的輸出
formatted 格式化 Generate生成 neatly 整潔的 full完全的
1 from name_function import get_formatted_name #從name_function.py中匯入get_formatted_name()
11-1城市和國家:編寫一個函式,它接受兩個形參:一個城市名和一個國家名。這個函式返回一個格式為City, Country的字串,如Santiago, Chile。將這個函式儲存在一個名為city _functions.py的模組中。建立一個名為test_cities.py的程式,對剛編寫的函式進行測試(別忘了,你需要匯入模組unittest以及要測試的函式)。編寫一個名為test_city_country()的方法,核實使用類似於'santiago'和'chile'這樣的值來呼叫前述函式時,得到的字串是正確的。執行test_cities.py,確認測試test_city_country()通過了。
1 #city_functions.py 2 def city_country(city_name, country_name, middle=','): # 3 """輸出城市名和國家名,例如Santiago, chile""" 4 full_city_country = city_name + middle + country_name 5 return full_city_country.title() 6 7 #city_name_country_name.py 8 from city_functions import city_country9 print("Enter 'q' at any time to quit.") 10 while True: 11 city_name = input("親輸入城市名: ") 12 if city_name == 'q': 13 break 14 country_name = input("親輸入國家名: ") 15 if country_name == 'q': 16 break 17 18 formatted_name = city_country(city_name, country_name) 19 print("\n城市和國家名的輸出結果: " + formatted_name) 20 21 22 #test_cityname_and_country_name.py 23 import unittest #匯入inttest模組中的工具 24 from city_functions import city_country 25 26 class NamesTestCase(unittest.TestCase): #這個命名可以隨意,但通常都與測試的函式相關而且要包含Test字樣 27 """測試city_functions.py""" #繼承TestCase類這樣Python才知道如何執行你編寫的測試,可以認為是一個介面將你的函式 28 #在另一個地方執行 29 def test_city_country_name(self): #這個測試檔案的方法要用test_為開頭,_test為開頭的方法在檔案執行時會自動執行 30 """能夠正確地處理像Santigo, Chile這類的名字嗎?""" 31 formatted_name = city_country('Santigo', 'Chile') #呼叫了要測試的函式,並將返回值儲存到formatted_name中 32 self.assertEqual(formatted_name, 'Santigo,Chile')#使用斷言方法assertEqual用來核實得到的結果是否與期望的結果一致 33 34 '''在這裡,我們知道get_formatted_name()應返回這樣的城市國家名,即 35 首字母為大寫,且它們之間有一個‘,’,因此我們期望formatted_name的值為Santigo,Chile。為檢查是否確實如此,我們呼叫unittest的方法assertEqual(),並向它傳遞formatted_ 36 name和'Santigo Chile'。程式碼行self.assertEqual(formatted_name, 'Janis Joplin')的意思是說:“將formatted_name的值同字串'Santigo,Chile'進行比較,如果它們相等,就萬事大吉,如果它 37 們不相等,跟我說一聲!''' 38 unittest.main()
11-2人口數量:修改前面的函式,使其包含第三個必不可少的形參population,並返回一個格式為City, Country – population xxx的字串,如Santiago, Chile –population 5000000。執行test_cities.py,確認測試test_city_country()未通過。修改上述函式,將形參population設定為可選的。再次執行test_cities.py,確認測試test_city_country()又通過了。再編寫一個名為test_city_country_population()的測試,核實可以使用類似於'santiago'、'chile'和'population=5000000'這 樣 的 值 來 調 用 這 個 函 數 。 再 次 運 行test_cities.py,確認測試test_city_country_population()通過了。
1 #寫到一般才看到是測試兩個。。一個可選,一個不可選 2 #不可選 3 def city_country_population(city_name, country_name, population='population-50000'): 4 """輸出城市名和國家名,例如Santiago, chile""" 5 full_city_country = (city_name + ', ' + 6 country_name + str(population)) 7 return full_city_country.title() 8 #city_country_population_name.py 9 from city_country_population import city_country_population 10 population = 'population-500000' 11 print("Enter 'q' at any time to quit.") 12 while True: 13 city_name = input("親輸入城市名: ") 14 if city_name == 'q': 15 break 16 country_name = input("親輸入國家名: ") 17 if country_name == 'q': 18 break 19 #population = input("請輸入人口數: ") 20 #if population == 'q': 21 #break 22 23 formatted_name = city_country_population(city_name, country_name, population) 24 print("\n城市和國家名和人口的輸出結果: " + formatted_name) 25 #可選 26 #city_country_population.py 27 def city_country_population(city_name, country_name, population): 28 """輸出城市名和國家名,例如Santiago, chile""" 29 full_city_country = (city_name + ', ' + 30 country_name + ' -population ' + population) 31 return full_city_country.title() 32 33 #city_country_population_name.py 34 from city_country_population import city_country_population 35 print("Enter 'q' at any time to quit.") 36 while True: 37 city_name = input("親輸入城市名: ") 38 if city_name == 'q': 39 break 40 country_name = input("親輸入國家名: ") 41 if country_name == 'q': 42 break 43 population = input("請輸入人口數: ") 44 if population == 'q': 45 break 46 47 formatted_name = city_country_population(city_name, country_name, population) 48 print("\n城市和國家名和人口的輸出結果: " + formatted_name) 49 50 #test_city_and_country_populations.py 51 import unittest 52 from city_country_population import city_country_population 53 54 class NamesTestCase(unittest.TestCase): 55 """測試city_functions.py""" 56 57 def test_city_country_population_name(self): 58 """能夠正確地處理像Santigo, Chile這類的名字嗎?""" 59 formatted_name = city_country_population('Asd', 'Asd', '456') 60 self.assertEqual(formatted_name, 'Asd, Asd -Population 456') 61 62 unittest.main() 63 #測試不通過的沒寫,因為太多不通過的方法了,沒有明確定義用啥
斷言方法
assertEqual(a, b) 核實a == b assertNotEqual(a, b) 核實a != b assertTrue(x) 核實x為True assertFalse(x) 核實x為False assertIn(item, list) 核實item在list中 assertNotIn(item, list) 核實item不在list中
11-3僱員:編寫一個名為Employee的類,其方法__init__()接受名、姓和年薪,並將它們都儲存在屬性中。編寫一個名為give_raise()的方法,它預設將年薪增加5000美元,但也能夠接受其他的年薪增加量。為Employee編寫一個測試用例,其中包含兩個測試方法:test_give_default_raise()和test_give_custom_raise()。使用方法setUp(),以免在每個測試方法中都建立新的僱員例項。執行這個測試用例,確認兩個測試都通過了。
1 #Employee.py 2 class Employee: 3 """收集打工人的資訊""" 4 def __init__(self, firstname, lastname, money): 5 """打工人初始化""" 6 self.firstname = firstname 7 self.lastname = lastname 8 self.money = money 9 def give_raise(self, addmoney=5000): 10 """應該加的錢(可能嗎)""" 11 self.money += addmoney 12 13 #測試用例 14 import inttest 15 from Employee import Employee #檔案Employee內的Employee類 16 class EmployeeTestCase(unittest.TestCase): #如果你在TestCase類中包含了方法setUp(),Python將先執行 17 # 它,再執行各個以test_打頭的方法 18 """測試不同打工人的資訊""" 19 def setUp(self): 20 """初始化打工人資訊""" #方法setUp(),讓我們只需建立這些物件一 21 # 次,並在每個測試方法中使用它們。 22 self.default = Employee('xiao', 'ma', 1000) #變數名包含字首self(即儲存在屬性中) 23 """預設加的薪水""" 24 self.default.give_raise() 25 self.assertEqual(self.default.money,6000) 26 def test_igive_coustom_raise(self): 27 """特殊加薪""" 28 self.default.give_raise(8000) 29 self.assertEqual(self.default.money,9000) 30 unittest.main()
小結:測試程式碼
參與工作量較大的專案時,你應對自己編寫的函式和類的重要行為進行測試,在專案早期,不要試圖去編寫全覆蓋的測試用例,除非有充分的理由這樣做。
使用模組unittest中的工具來為函式和類編寫測試;如何編寫繼承unittest.TestCase的類,以及如何編寫測試方法,以核實函式和類的行為符合預期;如何使用方法setUp()來根據類高效地建立例項並設定其屬性,以便在類的所有測試方法中都可使用它們。