閱讀《Python編程從入門到實踐》Day14

第十一章

編寫函數(shù)或類時,還可為其編寫測試昨凡。通過測試爽醋,可確定代碼面對各種輸入都能夠按要求的那樣工作。

1土匀、測試函數(shù)

下面是要用于測試的函數(shù)子房,它接受名和姓并返回整潔的姓名,存儲在文件name_function.py中:

def get_formatted_name(first, last):
    full_name = first + ' ' + last
    return full_name.title()

函數(shù)將名和姓合并成姓名就轧,在名和姓之間加上一個空格证杭,并將它們的首字母都大寫,再返回結果妒御。為核實函數(shù)是否像期望的那樣工作解愤,下面編寫一個使用這個函數(shù)的程序:

from name_function import get_formatted_name

print("Enter 'q' at any time to quit.")
while True:
    first = input("\nPlease give me a first name: ")
    if first == 'q':
        break
    last = input("Please give me a last name: ")
    if last == 'q':
        break

    formatted_name = get_formatted_name(first, last)
    print("\tNeatly formatted name: " + formatted_name + ".")

測試如下:

Enter 'q' at any time to quit.

Please give me a first name: janis
Please give me a last name: joplin
    Neatly formatted name: Janis Joplin.

Please give me a first name: bob
Please give me a last name: dylan
    Neatly formatted name: Bob Dylan.

Please give me a first name: q

從測試的結果可以知道,合并得到的姓名是正確的乎莉。如果現(xiàn)在需要添加處理中間名的功能送讲,就需要在保證不破壞原來功能的基礎上奸笤,添加新的功能,然后再進行測試哼鬓。這樣顯得就太繁瑣了监右,不過Python您提供了一種自動測試函數(shù)輸出的高效方式,可對相應的函數(shù)進行自動測試异希。

(1)單元測試和測試用例

Python標準庫中的模塊unittest提供了代碼測試工具健盒。單元測試用于核實函數(shù)的某個方面沒有問題;測試用例是一組單元測試称簿,這些單元測試一起核實函數(shù)在各種情形下的的行為都符合要求扣癣。良好的測試用例考慮到了函數(shù)可能收到的各種輸入,包含針對所有這些情形的的測試憨降。全覆蓋式測試用例包含一整套單元測試父虑,涵蓋了各種可能的函數(shù)使用方式。對于大型項目授药,要實現(xiàn)全覆蓋可能很難士嚎。通常,最初只要針對代碼的重要行為編寫測試即可烁焙,等項目被廣泛使用時再考慮全覆蓋航邢。

(2)可通過的測試

要為函數(shù)編寫測試用例,可先導入模塊unittest以及要測試的函數(shù)骄蝇,再創(chuàng)建一個unittest.TestCase的類膳殷,并編寫一系列方法對函數(shù)行為的不同方面進行測試。下面是只包含一個方法的測試用例:

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    def test_first_last_name(self):
        formatted_name = get_formatted_name('janis', 'joplin')
        self.assertEqual(formatted_name, 'Janis Joplin')

unittest.main()
# 輸出:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

首先九火,需要導入模塊unittest和要測試的函數(shù)赚窃。然后創(chuàng)建一個類,用于包含一系列針對被測試函數(shù)的單元測試岔激,這個類必須繼承unittest.TestCase類勒极,這樣Python才知道如何運行你編寫的測試。
這個類只包含了一個方法虑鼎,用于核實姓名能否被正確地格式化辱匿。在運行上述文件時,所有以test_大頭的方法都將自動運行炫彩。
這里使用了unittest類中最有用的功能之一:一個斷言方法匾七。斷言方法用來核實得到的結果是否與期望的結果一致。self.assertEqual()方法就是將第一個參數(shù)和第二個參數(shù)進行比較江兢。
在輸出的結果中昨忆,第一行的句點表明有一個測試通過了。接下來的一行指出Python運行了一個測試杉允,消耗的時間不到0.001秒邑贴。最后的OK表明該測試用例中的所有單元測試都通過了席里。

(3)不能通過的測試

我們故意只在函數(shù)中添加可以處理中間名的形參middle,而沒有修改測試用例中的實參奖磁。運行結果:

E
======================================================================
ERROR: test_first_last_name (__main__.NamesTestCase)
----------------------------------------------------------------------
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.002s

FAILED (errors=1)

由于測試沒有通過,返回了很多信息独旷。第一行輸出只有一個字母E署穗,它指出測試用例中有一個單元測試導致了錯誤寥裂。然后可以看到類中的函數(shù)導致了錯誤嵌洼。當測試用例包含很多單元測試時,準確知道那個測試沒通過至關重要封恰。在往下麻养,我們看到一個標準的traceback,它準確指出函數(shù)調用中出現(xiàn)了問題诺舔,因為它缺少了一個必不可少的位置實參鳖昌。最后顯示運行了一個單元測試,并指出整個測試用例都沒有通過低飒。

(4)測試未通過時怎么辦

測試未通過說明你編寫的新代碼有錯许昨,此時,不要修改測試褥赊,而應修復導致測試不能通過的代碼:檢查剛對函數(shù)所做的修改糕档,找出導致函數(shù)行為不符合預期的修改。
對于上述未能通過的測試拌喉,我們知道是新增的中間名參數(shù)導致的速那,所以可以讓中間名變?yōu)榭蛇x的,即添加默認值尿背,然后再適當?shù)靥砑觟f判斷語句端仰,就可以讓測試通過了。

(5)添加新測試

下面再編寫一個測試田藐,用于測試包含中間名的姓名荔烧。

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    def test_first_last_name(self):
        formatted_name = get_formatted_name('janis', 'joplin')
        self.assertEqual(formatted_name, 'Janis Joplin')

    def test_first_last_middle_name(self):
        formatted_name = get_formatted_name('wolfgang', 'mozart', 'amadeus')
        self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')

unittest.main()
# 輸出:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

這個新添加方法的方法名必須以test_開頭,這樣它才會在我們運行整個測試文件時自動運行汽久。在TestCase類中使用很長的方法名是可以的鹤竭;這些方法名必須具有描述性的,這才能讓你明白測試未通過時的輸出回窘;這些方法由Python自動調用诺擅,你根本不用編寫調用它們的代碼。

2啡直、測試類

(1)unittest Module中常用的6個斷言方法
方法 用途
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中

Python在unittest.TestCase類中提供了很多斷言方法烁涌。如果你認為應該滿足的條件實際上并不滿足苍碟,Python將引發(fā)異常。

(2)一個要測試的類

類的測試與函數(shù)的測死相似撮执,但也存在一些不同之處微峰。下面一個幫助管理匿名調查的類:

class AnonymousSurvey():
    def __init__(self, question):
        self.question = question
        self.responses = []
    
    def show_question(self):
        print(self.question)
    
    def store_response(self, new_response):
        self.responses.append(new_response)
    
    def show_results(self):
        print("Survey results:")
        for response in self.responses:
            print('- ' + response)

要創(chuàng)建這個類的實例,只需提供一個問題即可抒钱。下面編寫一個使用它的程序:

from survey import AnonymousSurvey

question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.show_question()
print("Enter 'q' at any time to quit.\n")
while True:
    response = input("Language: ")
    if response == 'q':
        break
    my_survey.store_response(response)

print("\nThank you to everyone who participated in the survey!")
my_survey.show_results()
# 輸出:
What language did you first learn to speak?
Enter 'q' at any time to quit.

Language: English
Language: Spanish
Language: English
Language: Mandarin
Language: q

Thank you to everyone who participated in the survey!
Survey results:
- English
- Spanish
- English
- Mandarin
(3)測試AnonymousSurvey類

下面編寫一個測試:如果用戶面對調查問題時只提供一個答案蜓肆,這個答案也能被妥善地存儲。

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    def test_store_single_response(self):
        question = "What language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        my_survey.store_response('English')
        self.assertIn('English', my_survey.responses)

unittest.main()
# 測試輸出:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

這里的測試與測試函數(shù)時類似谋币,這里的第一個方法驗證調查問題的單個答案被存儲后仗扬,會包含在調查結果列表中。要測試類的行為蕾额,需要創(chuàng)建其實例早芭。由輸出知道,測試順利通過了诅蝶。
下面來核實用戶提供三個答案時退个,它們也將被妥善地存儲。

    def test_store_three_responses(self):
        question = "What language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        responses = ['English', 'Spanish', 'Mandarih']
        for response in responses:
            my_survey.store_response(response)
            
        for response in responses:
            self.assertIn(response, my_survey.responses)
# 測試輸出:
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

在測試類中添加上述的方法调炬,有結果可知语盈,測試也順利通過了。但是這些測試有些重復的地方缰泡。下面使用unittest的另一項功能來提高它們的效率刀荒。

(4)方法setUp()

在前面的示例中,我們在每個測試的方法中都創(chuàng)建了一個AnonymousSurvey實例匀谣,并在每個方法中都創(chuàng)建了答案照棋。unittest.TestCase類中包含了方法setUp(),Python將先運行它武翎,再運行各個以test_開頭的方法烈炭。這樣,在你編寫的每個測試方法中都可使用在方法setUp()中創(chuàng)建的對象宝恶。
下面使用setUp()來創(chuàng)建一個調查對象和一組答案符隙,供兩個測試方法使用:

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    
    def setUp(self):
        question = "What language did you first learn to speak?"
        self.my_survey = AnonymousSurvey(question)
        self.responses = ['English', 'Spanish', 'Mandarih']
    
    def test_store_single_response(self):
        self.my_survey.store_response(self.responses[0])
        self.assertIn(self.responses[0], self.my_survey.responses)

    def test_store_three_responses(self):
        for response in self.responses:
            self.my_survey.store_response(response)
        for response in self.responses:
            self.assertIn(response, self.my_survey.responses)

unittest.main()

方法setUp()做了兩件事情:創(chuàng)建一個調查對象;創(chuàng)建一個答案列表垫毙。存儲這兩樣東西的變量名包含前綴self(即存儲在屬性中)霹疫,因此可在這個類的任何地方使用。
測試自己編寫的類時综芥,方法setUp()讓測試方法編寫起來更容易:可在setUp()方法中創(chuàng)建一系列實例并設置它們的屬性丽蝎,再在測試方法中直接使用這些實例。

注意:運行測試用例時,每完成一個單元測試屠阻,Python都打印一個字符:測試通過時打印一個句點红省;測試引發(fā)錯誤時打印一個E;測試導致斷言失敗時打印一個F国觉。這就是你運行測試用例時吧恃,在輸出的第一行中看到的句點和字符數(shù)量各不相同的原因。如果測試用例包含很多單元測試麻诀,需要運行很長時間痕寓,就可通過觀察這些結果來獲悉有多少個測試通過了。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蝇闭,一起剝皮案震驚了整個濱河市呻率,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丁眼,老刑警劉巖筷凤,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異苞七,居然都是意外死亡,警方通過查閱死者的電腦和手機挪丢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門蹂风,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乾蓬,你說我怎么就攤上這事惠啄。” “怎么了任内?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵撵渡,是天一觀的道長。 經常有香客問我死嗦,道長趋距,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任越除,我火速辦了婚禮节腐,結果婚禮上,老公的妹妹穿的比我還像新娘摘盆。我一直安慰自己翼雀,他們只是感情好,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布孩擂。 她就那樣靜靜地躺著狼渊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪类垦。 梳的紋絲不亂的頭發(fā)上狈邑,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天坦弟,我揣著相機與錄音,去河邊找鬼官地。 笑死酿傍,一個胖子當著我的面吹牛,可吹牛的內容都是我干的驱入。 我是一名探鬼主播赤炒,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼亏较!你這毒婦竟也來了莺褒?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤雪情,失蹤者是張志新(化名)和其女友劉穎遵岩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巡通,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡尘执,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宴凉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誊锭。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖弥锄,靈堂內的尸體忽然破棺而出丧靡,到底是詐尸還是另有隱情,我是刑警寧澤籽暇,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布温治,位于F島的核電站,受9級特大地震影響戒悠,放射性物質發(fā)生泄漏熬荆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一救崔、第九天 我趴在偏房一處隱蔽的房頂上張望惶看。 院中可真熱鬧,春花似錦六孵、人聲如沸纬黎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽本今。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冠息,已是汗流浹背挪凑。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逛艰,地道東北人躏碳。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像散怖,于是被迫代替她去往敵國和親菇绵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容

  • 編寫函數(shù)或類時镇眷,還可為其編寫測試咬最。通過測試,可確定代碼面對各種輸入都能夠按要求的那樣工作欠动。在程序中添加新代碼時永乌,你...
    Darren_Lin閱讀 5,240評論 1 5
  • 洞見SELENIUM自動化測試 寫在最前面:目前自動化測試并不屬于新鮮的事物,或者說自動化測試的各種方法論已經層出...
    厲鉚兄閱讀 6,714評論 3 47
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理具伍,服務發(fā)現(xiàn)翅雏,斷路器,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 感賞女兒下午提醒我不要再購買韓貨,薩徳系統(tǒng)讓女兒憤憤不平啼肩,家國情懷,政治抱負釀造了我家小憤青衙伶,大刀闊斧地分析了一...
    利利lili閱讀 190評論 0 5
  • 今天是“雙十二”祈坠,跟風上淘寶逛逛,結果一不留神沒克制住矢劲,買了兩雙童鞋赦拘,兩個兒童水杯,兩只橡木高腳凳芬沉,和一瓶護膚水躺同。...
    成樂閱讀 125評論 0 1