編寫函數或類時,還可為其編寫測試。通過測試,可確定代碼面對各種輸入都能夠按要求的那樣工作。在程序中添加新代碼時,你也可以對其進行測試,確認它們不會破壞程序既有的行為。
使用Python模塊unittest 中的工具來測試代碼
【注意】:測試代碼所在的類,必須繼承unittest.TestCase繼承類
(1)單元測試與測試用例
Python標准庫中的模塊unittest提供了代碼測試工具。
1、單元測試用於核實函數的某個方面沒有問題;
2、測試用例是一組單元測試,這些單元測試一起核實函數在各種情形下的行為都符合要求。
良好的測試用例考慮到了函數可能收到的各種輸入,包含針對所有這些情形的測試。
3、全覆蓋式測試用例包含一整套單元測試,涵蓋了各種可能的函數使用方式。
對於大型項目,要實現全覆蓋可能很難。通常,最初只要針對代碼的重要行為編寫測試即可,等項目被廣泛使用時再考慮全覆蓋。(一般都做不到)
(2)可通過的測試
創建測試用例的語法需要一段時間才能習慣,但測試用例創建後,再添加針對函數的單元測試就很簡單了。
【編寫函數測試模塊語法】:
要為函數編寫測試用例,可先導入模塊unittest 以及要測試的函數,再創建一個繼承unittest.TestCase 的類,並編寫一系列方法對函數行為的不同方面進行測試。
import unittest #導入測試模塊
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase): #必須繼承unittest.TestCase類
"""測試name_function.py"""
def test_first_last_name(self): # 所有以test打頭的方法都將被Python自動執行(測試類中,繼承unittest.TestCase的類表示測試類)
"""能夠正確地處理像Janis Joplin這樣的姓名嗎?"""
formatted_name = get_formatted_name('janis', 'joplin')#需測試的函數(自定義函數)得到的結果
self.assertEqual(formatted_name, 'Janis Joplin') #調用unittest 的方法assertEqual() 斷言方法
#將自己的函數得出的解果與正確結果進行比較,若正確,則輸出ok,反之輸出error
unittest.main()#運行測試代碼
(3)斷言方法
使用了unittest 類最有用的功能之一:一個斷言方法。斷言方法用來核實得到的結果是否與期望的結果一致,即檢查你認為應該滿足的條件是否確實滿足。
例如:
self.assertEqual(formatted_name, ‘Janis Joplin’)
我們調用unittest 的方法assertEqual() ,並向它傳遞formatted_name 和’Janis Joplin’ 。代碼行self.assertEqual(formatted_name, ‘Janis Joplin’) 的意思是 說:“將formatted_name 的值同字符串’Janis Joplin’ 進行比較。
(4)重復進行測試
修改自己的函數,既可實現重復測試
(5)運行測試文件
代碼行unittest.main()讓Python運行這個文件中的測試代碼。
A、通過測試結果如下:
. #句點代表有一個測試通過了 n個點,代表n個測試通過
----------------------------------------------------------------------
Ran 1 test in 0.000s #表示Python運行了一個測試。消耗時間不到0.001秒
OK #表示該測試案例中所有的單元測試都通過了
B、不能通過的測試結果如下:
E #輸出只有一個字母E,表示測試用例中只有一個單元測試導致了錯誤。 即出現n個字母E,即有n個單測試導致了錯誤
======================================================================
ERROR: test_first_last_name (__main__.NamesTestCase) #錯誤位置 NamesTestCase 中的test_first_last_name() 導致了錯誤
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_name_function.py", line 8, in test_first_last_name
formatted_name = get_formatted_name('janis', 'joplin')
TypeError: get_formatted_name() missing 1 required positional argument: 'last'
#錯誤原因 缺少一個必不可少的位置實參
-----------------------------------------------------------------------
Ran 1 test in 0.000s #運行了一個測試單元
FAILED (errors=1) #指出整個測試用例都未通過 因為運行該測試用例時發生了一個錯誤
#這條消息位於輸出末尾,讓你一眼就能看到
(6)測試未通過則麼辦?
如果你檢查的條件沒錯,測試通過了意味著函數的行為是對的,而測試未通過意味著你編寫的新代碼有錯。因此,測試未通過時,不要修改測試,而應修復導致測試不能通過的代碼:檢查剛對函數所做的修改,找出導致函數行為不符合預期的修改。
(7)添加新測試函數
可根據具體的項目開發需求,編寫多個測試函數,進行方法測試
(8)類測試
前面主要介紹了針對單個函數的測試,下面來編寫針對類的測試。很多程序中都會用到類,因此能夠證明你的類能夠正確地工作會大有裨益。如果針對類的測試通過 了,你就能確信對類所做的改進沒有意外地破壞其原有的行為。
A、常見斷言方法: 必須在unittest類中使用,所以必須繼承
Python在unittest.TestCase 類中提供了很多斷言方法。前面說過,斷言方法檢查你認為應該滿足的條件是否確實滿足。如果該條件確實滿足,你對程序行為的假設就得到了 確認,你就可以確信其中沒有錯誤。如果你認為應該滿足的條件實際上並不滿足,Python將引發異常。
上表中主要舉例的方法,必須在unittest.TestCase類中使用
B、測試類的實現
類的測試與函數的測試相似——你所做的大部分工作都是測試類中方法的行為,但存在一些不同之處。
import unittest #導入測試模塊
from survey import AnonymousSurvey
class TestAnonmyousSurvey(unittest.TestCase): #必須繼承unittest.TestCase類
"""針對AnonymousSurvey類的測試"""
def test_store_single_response(self):#test開頭 自動運行
"""測試單個答案會被妥善地存儲"""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.store_response('English')
self.assertIn('English', my_survey.responses) #斷言方法 核實item是否在list中 返回true or false
unittest.main()#測試函數啟動
【小tips】:
類測試和函數測試主要過程是一樣的,類測試是通過測試類中的每一個函數,進行類中每一個方法的測試,來實現最終的類測試。
在調用方法時,需先對類進行實例化,然後通過實例化的對象調用類中的方法,進行測試。
(9)方法setUp()
unittest.TestCase 類包含方法setUp(),讓我們只需創建這些對象一次,並在每個測試方法中使用它們。如果你在TestCase 類中包含了方法setUp() ,Python將先運行它,再運行各個以test_打頭的方法。
重寫unittest.TestCase類(父類)中的setUp() 函數,在該函數中創建對象。使用setup()函數,可以簡化創建對象的步驟,在每次運行測試代碼時,Python將最先運行setup()函數,然後再運行各個test_打頭的方法。
【優勢】:
測試自己編寫的類時,方法setUp() 讓測試方法編寫起來更容易:可在setUp() 方法中創建一系列實例並設置它們的屬性,再在測試方法中直接使用這些實例。相比於在每個 測試方法中都創建實例並設置其屬性,這要容易得多。