針對單個函數的測試
1.要測試的函數
name_function.py
1 def get_formatted_name(first, last): 2 """接受名和姓並返回整潔的姓名""" 3 full_name = first + ' ' + last 4 return full_name.title() 5 6 #函數 get_formatted_name() 將名和姓合並成姓名,在名和姓之間加上一個空格,並將它們的首字母都大寫,再返回結果。
2.編寫測試用例
test_name_function.py
1 import unittest #導入 unittest 模塊 2 from name_function import get_formatted_name #導入要測試的函數 3 4 class NamesTestCase(unittest.TestCase): #測試用例:創建一個繼承 unittest.TestCase 的類,包含一系列針對 get_formatted_name 的單元測試 5 """ 測試 name_function.py""" 6 7 def test_first_last_name(self): #編寫方法對函數的行為進行測試 8 """ 檢查函數 get_formatted_name() 在給定名和姓時能否正確地工作 """ 9 formatted_name = get_formatted_name('janis', 'joplin') #在這個方法中,我們調用了要測試的函數,並存儲了要測試的返回值。 10 self.assertEqual(formatted_name, 'Janis Joplin') #通過斷言,將 formatted_name 的值同字符串 'Janis Joplin' 進行比較。 11 12 unittest.main() #運行這個文件中的測試,運行 testname_function.py 時,所有以 test_ 打頭的方法都將自動運行。
3.運行結果(測試通過)
1 . # . 表示有一個測試通過了 2 ---------------------------------------------------------------------- 3 Ran 1 test in 0.000s # 表示執行了一個測試,執行時間0.000s 4 OK #表示該測試用例中的所有單元測試都通過了
1.修改要測試的函數
name_function.py
1 def get_formatted_name(first, middle, last): 2 """ 接受名和姓並返回整潔的姓名 """ 3 full_name = first + ' ' + middle + ' ' + last 4 return full_name.title() 5 6 #函數 get_formatted_name() 將姓、中間名、名合並成姓名。
2.運行測試用例(測試未通過)
1 E #測試用例中有一個單元測試導致了錯誤 2 ====================================================================== 3 ERROR: test_first_last_name (__main__.NamesTestCase) # NamesTestCase 中的 test_first_last_name() 導致了錯誤 4 ---------------------------------------------------------------------- 5 Traceback (most recent call last): 6 File "test_name_function.py", line 8, in test_first_last_name 7 formatted_name = get_formatted_name('janis', 'joplin') 8 TypeError: get_formatted_name() missing 1 required positional argument: 'last' 9 # traceback指出:函數調用 get_formatted_name('janis', 'joplin') 有問題,因為它缺少一個必不可少的位置實參。 10 11 ---------------------------------------------------------------------- 12 Ran 1 test in 0.000s 13
14 FAILED (errors=1) #整個測試用例都未通過,因為運行該測試用例時發生了一個錯誤
1.修改要測試的函數
name_function.py
1 def get_formatted_name(first, last, middle=''): #將形參 middle 移到形參列表末尾,默認值為一個空字符串,變成可選項 2 """ 接受名、中間名、姓並返回整潔的姓名 """ 3 if middle: #添加 if 測試,以便根據是否提供了中間名相應地創建姓名 4 full_name = first + ' ' + middle + ' ' + last 5 else: 6 full_name = first + ' ' + last 7 return full_name.title()
2.編寫測試用例,新增單元測試
test_name_function.py
1 import unittest 2 from name_function import get_formatted_name 3 4 class NamesTestCase(unittest.TestCase): 5 """ 測試 name_function.py """ 6 7 def test_first_last_name(self): 8 """ 能夠正確地處理像 Janis Joplin 這樣的姓名嗎? """ 9 formatted_name = get_formatted_name('janis', 'joplin') 10 self.assertEqual(formatted_name, 'Janis Joplin') 11 12 def test_first_last_middle_name(self): #方法名必須是描述性的,這樣測試未通過時,才知道是哪種行為未通過 13 """ 能夠正確地處理像 Wolfgang Amadeus Mozart 這樣的姓名嗎? """ 14 formatted_name = get_formatted_name( 15 'wolfgang', 'mozart', 'amadeus') 16 self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart') 17 18 unittest.main()
3.運行結果(測試通過)
1 .. 2 ---------------------------------------------------------------------- 3 Ran 2 tests in 0.000s 4 5 OK
針對類的測試
1.要測試的類
survey.py
1 class AnonymousSurvey(): 2 """ 收集匿名調查問卷的答案 """ 3 4 def __init__(self, question): 5 self.question = question #存儲一個問題 6 self.responses = [] #創建一個空列表,用於存儲答案 7 8 def show_question(self): 9 """ 打印調查問題 """ 10 print(question) 11 12 def store_response(self, new_response): 13 """ 在答案列表中添加新答案 """ 14 self.responses.append(new_response) 15 16 def show_results(self): 17 """ 將存儲在列表中的答案都打印出來 """ 18 print("Survey results:") 19 for response in responses: 20 print('- ' + response)
2.編寫測試用例
test_survey.py
1 import unittest #導入unittest 2 from survey import AnonymousSurvey #導入要測試的類 3 4 class TestAnonmyousSurvey(unittest.TestCase): 5 """ 針對 AnonymousSurvey 類的測試 """ 6 7 def test_store_single_response(self): 8 """ 測試單個答案會被妥善地存儲 """ 9 question = "What language did you first learn to speak?" 10 my_survey = AnonymousSurvey(question) #創建實例 11 my_survey.store_response('English') #調用方法 store_response() 存儲單個答案 English 12 13 self.assertIn('English', my_survey.responses) 14 15 def test_store_three_responses(self): 16 """ 測試三個答案會被妥善地存儲 """ 17 question = "What language did you first learn to speak?" 18 my_survey = AnonymousSurvey(question) 19 responses = ['English', 'Spanish', 'Mandarin'] 20 for response in responses: 21 my_survey.store_response(response) 22 23 for response in responses: 24 self.assertIn(response, my_survey.responses) 25 26 unittest.main()
3.運行結果(測試通過)
1 .. 2 ---------------------------------------------------------------------- 3 Ran 2 tests in 0.000s 4 5 OK
1.方法 setUp()
如果在 TestCase 類中包含了方法 setUp() , Python 將先運行它,再運行各個以 test_ 打頭的方法。
所以如果每個方法中都要創建同一個實例,那么直接在方法 setUp() 中創建這些對象,就可以在每個測試方法中使用它們。
1 import unittest 2 from survey import AnonymousSurvey 3 4 class TestAnonymousSurvey(unittest.TestCase): 5 """ 針對 AnonymousSurvey 類的測試 """ 6 7 def setUp(self): 8 """ 創建一個調查對象和一組答案,供使用的測試方法使用 """ 9 question = "What language did you first learn to speak?" 10 self.my_survey = AnonymousSurvey(question) #創建一個調查對象 11 self.responses = ['English', 'Spanish', 'Mandarin'] #創建一個答案列表 12 13 def test_store_single_response(self): 14 """ 測試單個答案會被妥善地存儲 """ 15 self.my_survey.store_response(self.responses[0]) 16 self.assertIn(self.responses[0], self.my_survey.responses) 17 #存儲這兩樣東西的變量名包含前綴 self (即存儲在屬性中),因此可在這個類的任何地方使用。 18 19 def test_store_three_responses(self): 20 """ 測試三個答案會被妥善地存儲 """ 21 for response in self.responses: 22 self.my_survey.store_response(response) 23 for response in self.responses: 24 self.assertIn(response, self.my_survey.responses) 25 26 unittest.main() 27 28 #編寫測試類時,可在 setUp() 方法中創建一系列實例並設置它們的屬性,再在測試方法中直接使用這些實例。
2.運行結果分析
1 #運行測試用例時,每完成一個單元測試, Python 都打印一個字符: 2 3 ''' . 測試通過 4 E 測試引發錯誤 5 F 測試導致斷言失敗 '''
實例
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 """ 4 @desc: 測試126郵箱的登陸功能 5 1.使用公共方法public.login 6 2.將測試數據放在xml文件中,使用數據驅動(/test_data/login.xml) 7 3.這里使用xml.dom.minidom讀取xml數據 8 """ 9 import unittest 10 import xml.dom.minidom 11 import os 12 import sys 13 from selenium import webdriver 14 15 cur_dir = os.getcwd() 16 sys.path.append(cur_dir.split(r'\test_case')[0]) 17 18 from public import login 19 20 fpath = cur_dir.split('test_case')[0] + 'test_data' + os.path.sep + 'login.xml' 21 22 # 打開 xml 文檔 23 dom = xml.dom.minidom.parse(fpath) 24 25 # 得到文檔元素對象 26 root = dom.documentElement 27 28 29 class TestLogin(unittest.TestCase): 30 def setUp(self): 31 self.driver = webdriver.Firefox() 32 self.driver.implicitly_wait(30) 33 logins = root.getElementsByTagName('url') 34 self.base_url = logins[0].firstChild.data 35 self.verificationErrors = [] 36 37 # 用例1:用戶名、密碼為空 38 def test_null(self): 39 driver = self.driver 40 driver.get(self.base_url) 41 # 讀取xml中的數據 42 logins = root.getElementsByTagName('null') 43 # 獲得 null 標簽的 username、password 屬性值 44 username = logins[0].getAttribute("username") 45 password = logins[0].getAttribute("password") 46 prompt_info = logins[0].firstChild.data 47 # 登錄 48 login.login(self, username, password) 49 # 獲取斷言信息進行斷言 50 text = driver.find_element_by_xpath("//div[@class='error-tt']/p").text 51 self.assertEqual(text, prompt_info) 52 53 # 用例2:用戶名為空 54 def test_user_null(self): 55 driver = self.driver 56 driver.get(self.base_url) 57 logins = root.getElementsByTagName('user_null') 58 # 獲得 user_null 標簽的 username、passwrod 屬性值 59 username = logins[0].getAttribute("username") 60 password = logins[0].getAttribute("password") 61 prompt_info = logins[0].firstChild.data 62 # 登錄 63 login.login(self, username, password) 64 # 獲取斷言信息進行斷言 65 text = driver.find_element_by_xpath("//div[@class='error-tt']/p").text 66 self.assertEqual(text, prompt_info) 67 68 # 用例3:密碼為空 69 def test_pwd_null(self): 70 driver = self.driver 71 driver.get(self.base_url) 72 logins = root.getElementsByTagName('pwd_null') 73 # 獲得 pwd_null 標簽的 username、passwrod 屬性值 74 username = logins[0].getAttribute("username") 75 password = logins[0].getAttribute("password") 76 prompt_info = logins[0].firstChild.data 77 # 登錄 78 login.login(self, username, password) 79 # 獲取斷言信息進行斷言 80 text = driver.find_element_by_xpath("//div[@class='error-tt']/p").text 81 self.assertEqual(text, prompt_info) 82 83 # 用例4:錯誤的用戶名和密碼 84 def test_error(self): 85 driver = self.driver 86 driver.get(self.base_url) 87 logins = root.getElementsByTagName('error') 88 # 獲得 error 標簽的 username、passwrod 屬性值 89 username = logins[0].getAttribute("username") 90 password = logins[0].getAttribute("password") 91 prompt_info = logins[0].firstChild.data 92 # 登錄 93 login.login(self, username, password) 94 # 獲取斷言信息進行斷言 95 text = driver.find_element_by_xpath("//div[@class='error-tt']/p").text 96 self.assertEqual(text, prompt_info) 97 98 def tearDown(self): 99 self.driver.quit() 100 self.assertEqual([], self.verificationErrors) 101 102 103 if __name__ == "__main__": 104 unittest.main()
文章參考自:Python編程:從入門到實踐.pdf