Python程式設計從入門到實踐筆記——測試程式碼
11 測試程式碼
編寫函式或類時,還可為其編寫測試。通過測試,可確定程式碼面對各種輸入都能夠按要求的那樣工作。在程式中新增新程式碼時,你也可以對其進行測試,確認它們不會破壞程式既有的行為。
在本章中,你將學習如何使用Python模組unittest
中的工具來測試程式碼。你將學習編寫測試用例,核實一系列輸入都將得到預期的輸出。你將看到測試通過了是什麼樣子,測試未通過又是什麼樣子,還將知道測試未通過如何有助於改進程式碼。你將學習如何測試函式和類,並將知道該為專案編寫多少個測試。
"""11.1 測試函式"""
"""
下面是一個簡單的函式,它接受名和姓並返回整潔的姓名:
"""
"""name_function.py"""
def get_formatted_name(first, last):
"""Generate a neatly formatted full name."""
full_name = first + ' ' + last
return full_name.title()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
"""
為核實get_formatted_name()像期望的那樣工作,我們來編寫一個
使用這個函式的程式。程式names.py讓使用者輸入名和姓,並顯示整潔的全名:
"""
"""names.py"""
from name_function import get_formatted_name
print "Enter 'q' at any time to quit."
while True:
first = raw_input("\nPlease give me a first name: ")
if first == 'q':
break
last = raw_input("Please give me a last name: ")
if last == 'q':
break
formatted_name = get_formatted_name(first, last)
print("\tNeatly formatted name: " + formatted_name + '.')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
從上述輸出可知,合併得到的姓名正確無誤。現在假設我們要修改get_formatted_name()
,使其還能夠處理中間名。這樣做時,我們要確保不破壞這個函式處理只有名和姓的姓名的方式。為此,我們可以在每次修改get_formatted_name()
後都進行測試:執行程式names.py,並輸入像Janis Joplin這樣的姓名,但這太煩瑣了。
所幸Python提供了一種自動測試函式輸出的高效方式。倘若我們對get_formatted_name()
進行自動測試,就能始終信心滿滿,確信給這個函式提供我們測試過的姓名時,它都能正確地工作。
11.1.1 單元測試和測試用例
Python標準庫中的模組unittest
提供了程式碼測試工具。
單元測試用於核實函式的某個方面沒有問題;測試用例是一組單元測試,這些單元測試一起核實函式在各種情形下的行為都符合要求。
良好的測試用例考慮到了函式可能收到的各種輸入,包含針對所有這些情形的測試。全覆蓋式測試用例包含一整套單元測試,涵蓋了各種可能的函式使用方式。對於大型專案,要實現全覆蓋可能很難。通常,最初只要針對程式碼的重要行為編寫測試即可,等專案被廣泛使用時再考慮全覆蓋。
11.1.2 可通過的測試
要為函式編寫測試用例,可先匯入模組unittest
以及要測試的函式,再建立一個繼承unittest.TestCase
的類,並編寫一系列方法(需要以test或Test開頭)對函式行為的不同方面進行測試。 下面是一個只包含一個方法的測試用例,它檢查函式get_formatted_name()
在給定名和姓時能否正確地工作:
"""test_name_function.py"""
import unittest
#from name_function import get_formatted_name
def get_formatted_name(first, last):
"""Generate a neatly formatted full name."""
full_name = first + ' ' + last
return full_name.title()
class NamesTestCase(unittest.TestCase): # 這個類必須繼承unittest.TestCase類
"""測試name_function.py"""
def test_first_last_name(self):
formatted_name = get_formatted_name('janis', 'joplin')
# ①
self.assertEqual(formatted_name, 'Janis Joplin')
if __name__ == '__main__':
# unittest.main()
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
.
Janis Joplin
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
①處,我們使用了unittest
類最有用的功能之一:一個斷言方法。斷言方法用來核實得到的結果是否與期望的結果一致。
在這裡,我們期望formatted_name
的值為"Janis Joplin"
。為檢查是否確實如此,我們呼叫unittes
t的方法assertEqual()
,並向它傳遞formatted_name
和"Janis Joplin"
進行比較。程式碼行unittest.main()
讓Python執行這個檔案中的測試。
說明:
書中原始碼在本地可以執行,但是在jupyter notebook中執行報錯”AttributeError: ‘module’ object has no attribute”,看到Stack Overflow上的問答,參考修改後可以在jupyter notebook中執行。
unittest.main(argv=['first-arg-is-ignored'], exit=False)
unittest.main(argv=['ignored', '-v'], exit=False)
1.1.3 不能通過的測試
修改get_formatted_name(),使其能夠處理中間名,但這樣做時,故意讓這個函式無法正確地處理像Janis Joplin這樣只有名和姓的姓名。
下面是函式get_formatted_name()的新版本,它要求通過一個實參指定中間名:
"""name_function.py"""
def get_formatted_name(first, middle, last):
"""生成整潔的姓名"""
full_name = first + ' ' + middle + ' ' + last
return full_name.title()
- 1
- 2
- 3
- 4
- 5
執行程式test_name_function.py
"""test_name_function.py"""
import unittest
#from name_function import get_formatted_name
def get_formatted_name(first, middle, last):
"""生成整潔的姓名"""
full_name = first + ' ' + middle + ' ' + last
return full_name.title()
class NamesTestCase(unittest.TestCase): # 這個類必須繼承unittest.TestCase類
"""測試name_function.py"""
def test_first_last_name(self):
formatted_name = get_formatted_name('janis', 'joplin')
self.assertEqual(formatted_name, 'Janis Joplin')
if __name__ == '__main__':
# unittest.main()
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
E
E
ERROR: test_first_last_name (main.NamesTestCase)
Traceback (most recent call last): File “<ipython-input-22-c6f1d3890843>”, line 15, in test_first_last_name formatted_name = get_formatted_name(‘janis’, ‘joplin’) TypeError: get_formatted_name() takes exactly 3 arguments (2 given)
Ran 1 test in 0.041s
FAILED (errors=1)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
11.1.4 測試未通過時怎麼辦
測試未通過時怎麼辦呢?如果你檢查的條件沒錯,測試通過了意味著函式的行為是對的,而測試未通過意味著你編寫的新程式碼有錯。因此,測試未通過時,不要修改測試,而應修復導致測試不能通過的程式碼:檢查剛對函式所做的修改,找出導致函式行為不符合預期的修改。
在這個示例中,get_formatted_name()
以前只需要兩個實參——名和姓,但現在它要求提供名、中間名和姓。就這裡而言,最佳的選擇是讓中間名變為可選的。這樣做後,使用類似於Janis Joplin的姓名 進 行 測 試 時 , 測 試 就 會 通 過 了 , 同 時 這 個 函 數 還 能 接 受 中 間 名 。 下 面 來 修 改get_formatted_name()
,將中間名設定為可選的,然後再次執行這個測試用例。如果通過了,我們接著確認這個函式能夠妥善地處理中間名。
將中間名設定為可選的,可在函式定義中將形參middle移到形參列表末尾,並將其預設值指定為一個空字串。我們還要新增一個if測試,以便根據是否提供了中間名相應地建立姓名:
"""name_function.py"""
def get_formatted_name(first, last, middle=''):
"""Generate a neatly-formatted full name."""
if middle:
full_name = first + ' ' + middle + ' ' + last
else:
full_name = first + ' ' + last
return full_name.title()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
再次執行test_name_function.py:
"""test_name_function.py"""
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase): # 這個類必須繼承unittest.TestCase類
"""測試name_function.py"""
def test_first_last_name(self):
formatted_name = get_formatted_name('janis', 'joplin')
print formatted_name
self.assertEqual(formatted_name, 'Janis Joplin')
if __name__ == '__main__':
# unittest.main()
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
.
Janis Joplin
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
11.1.5 新增新測試
確定get_formatted_name()
又能正確地處理簡單的姓名後,我們再編寫一個測試,用於測試包含中間名的姓名。為此,我們在NamesTestCase
類中再新增一個方法:
"""test_name_function.py"""
import unittest
from name_function import get_formatted_name
class NamesTestCase(unittest.TestCase): # 這個類必須繼承unittest.TestCase類
"""測試name_function.py"""
def test_first_last_name(self):
formatted_name = get_formatted_name('janis', 'joplin')
print formatted_name
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')
if __name__ == '__main__':
# unittest.main()
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
...
Janis Joplin
----------------------------------------------------------------------
Ran 3 tests in 0.007s
OK
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
我們將這個方法命名為test_first_last_middle_name()
。方法名必須以test_打頭,這樣它才會在我們執行test_name_function.py時自動執行。這個方法名清楚地指出了它測試的是get_formatted_name()
的哪個行為,這樣,如果該測試未通過,我們就會馬上知道受影響的是哪種型別的姓名。在TestCase
類中使用很長的方法名是可以的;這些方法的名稱必須是描述性的,這才能讓你明白測試未通過時的輸出;這些方法由Python自動呼叫,你根本不用編寫呼叫它們的程式碼。
為測試函式get_formatted_name()
,我們使用名、姓和中間名呼叫它,再使用assertEqual()
檢查返回的姓名是否與預期的姓名(名、中間名和姓)一致。
習題11-1 城市和國家:
編寫一個函式,它接受兩個形參:一個城市名和一個國家名。這個函式返回一個格式為 City, Country
的字串,如 Santiago, Chile
。將這個函式儲存在一個名為 city_functions.py 的模組中。
建立一個名為 test_cities.py 的程式,對剛編寫的函式進行測試(別忘了,你需要匯入模組 unittest
以及要測試的函式)。編寫一個名為 test_city_country()
的方法,核實使用類似於 ‘santiago’ 和 ‘chile’ 這樣的值來呼叫前述函式時,得到的字串是正確的。
執行 test_cities.py,確認測試 test_city_country()
通過了。
"test_cities.py"
import unittest
from city_functions import city_country
class CityCountryTest(unittest.TestCase):
def test_city_country(self):
formatted_string = city_country('santiago', 'chile')
self.assertEqual(formatted_string, 'Santiago, Chile')
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
...
Janis Joplin
----------------------------------------------------------------------
Ran 3 tests in 0.007s
OK
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
習題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()
通過了。
"test_cities.py"
"加一個形參population"
import unittest
from city_functions import city_country
class CityCountryTest(unittest.TestCase):
def test_city_country(self):
formatted_string = city_country('santiago', 'chile')
self.assertEqual(formatted_string, 'Santiago, Chile')
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
E
E
ERROR: test_city_country (main.CityCountryTest)
Traceback (most recent call last): File “<ipython-input-1-40099b039d57>”, line 9, in test_city_country formatted_string = city_country(‘santiago’, ‘chile’) TypeError: city_country() takes exactly 3 arguments (2 given)
Ran 1 test in 0.002s
FAILED (errors=1)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
"test_cities.py"
"形參可選"
import unittest
from city_functions import city_country
class CityCountryTest(unittest.TestCase):
def test_city_country(self):
formatted_string = city_country('santiago', 'chile')
self.assertEqual(formatted_string, 'Santiago, Chile')
if __name__ == '__main__':
unittest.main(argv=['first_arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
.
.
Ran 1 test in 0.002s
OK
- 1
- 2
- 3
- 4
- 5
"test_cities.py"
"測試人口"
import unittest
from city_functions import city_country
class CityCountryTest(unittest.TestCase):
def test_city_country(self):
formatted_string = city_country('santiago', 'chile')
self.assertEqual(formatted_string, 'Santiago, Chile')
def test_city_country_population(self):
formatted_string = city_country('santiago', 'chile', 5000000)
self.assertEqual(formatted_string, 'Santiago, Chile - population 5000000')
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
…
…
Ran 2 tests in 0.005s
OK
- 1
- 2
- 3
- 4
- 5
11.2 測試類
11.2.1 各種斷言方法
Python在unittest.TestCase
類中提供了很多斷言方法。表中描述了6個常用的斷言方法。使用這些方法可核實返回的值等於或不等於預期的值、返回的值為True
或False
、返回的值在列表中或不在列表中。你只能在繼承unittest.TestCase
的類中使用這些方法。
方法 | 用途 |
---|---|
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.2.2 一個要測試的類 """
"""survey.py"""
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
"""編寫一個使用AnonymousSurvey類的程式"""
"""language_survey.py"""
from survey import AnonymousSurvey
#定義一個問題,並建立一個表示調查的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 = raw_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()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
"""11.2.3 測試 AnonymousSurvey 類 """
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey."""
def test_store_single_response(self):
"""Test that a single response is stored properly."""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.store_response('English')
self.assertIn('English', my_survey.responses)
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
.
.
Ran 1 test in 0.001s
OK
- 1
- 2
- 3
- 4
- 5
這很好,但只能收集一個答案的調查用途不大。下面來核實使用者提供三個答案時,它們也將被妥善地儲存。為此,我們在TestAnonymousSurvey
中再新增一個方法:
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey."""
def test_store_single_response(self):
"""Test that a single response is stored properly."""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.store_response('English')
self.assertIn('English', my_survey.responses)
def test_store_three_responses(self):
"""Test that three individual responses are stored properly."""
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)
responses = ['English', 'Spanish', 'Mandarin']
for response in responses:
my_survey.store_response(response)
for response in responses:
self.assertIn(response, my_survey.responses)
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
…
…
Ran 2 tests in 0.004s
OK
- 1
- 2
- 3
- 4
- 5
效果很好,但這些測試有些重複的地方。下面使用unittest
的另一項功能來提高它們的效率。
11.2.4 方法 setUp()
在前面的test_survey.py中,我們在每個測試方法中都建立了一個 AnonymousSurvey
例項,並在每個方法中都建立了答案。unittest.TestCase
類包含方法 setUp()
,讓我們只需建立這些物件一次,並在每個測試方法中使用它們。如果你在 TestCase
類中包含了方法 setUp()
,Python將先執行它,再執行各個以test_打頭的方法。這樣,在你編寫的每個測試方法中都可使用在方法 setUp()
中建立的物件了。
下面使用setUp()
來建立一個調查物件和一組答案,供方法test_store_single_response()
和test_store_three_responses()
使用:
import unittest
from survey import AnonymousSurvey
class TestAnonymousSurvey(unittest.TestCase):
"""Tests for the class AnonymousSurvey."""
def setUp(self):
"""
Create a survey and a set of responses for use in all test methods.
"""
question = "What language did you first learn to speak?"
self.my_survey = AnonymousSurvey(question)
self.responses = ['English', 'Spanish', 'Mandarin']
def test_store_single_response(self):
"""Test that a single response is stored properly."""
self.my_survey.store_response(self.responses[0])
self.assertIn(self.responses[0], self.my_survey.responses)
def test_store_three_responses(self):
"""Test that three individual responses are stored properly."""
for response in self.responses:
self.my_survey.store_response(response)
for response in self.responses:
self.assertIn(response, self.my_survey.responses)
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
…
…
Ran 2 tests in 0.003s
OK
- 1
- 2
- 3
- 4
- 5
方法setUp()
做了兩件事情:建立一個調查物件;建立一個答案列表。儲存這兩樣東西的變數名包含字首self
(即儲存在屬性中),因此可在這個類的任何地方使用。這讓兩個測試方法都更簡單,因為它們都不用建立調查物件和答案。方法test_store_single_response()
核 實 self.responses
中 的 第 一 個 答 案 ——self.responses[0]
—— 被 妥 善 地 存 儲 , 而 方 法test_store_three_response()
核實self.responses
中的全部三個答案都被妥善地儲存。
再次執行test_survey.py時,這兩個測試也都通過了。如果要擴充套件AnonymousSurvey
,使其允許每位使用者輸入多個答案,這些測試將很有用。修改程式碼以接受多個答案後,可執行這些測試,確認儲存單個答案或一系列答案的行為未受影響。
測試自己編寫的類時,方法setUp()
讓測試方法編寫起來更容易:可在setUp()
方法中建立一系列例項並設定它們的屬性,再在測試方法中直接使用這些例項。相比於在每個測試方法中都建立例項並設定其屬性,這要容易得多。
注意: 執行測試用例時,每完成一個單元測試,Python都列印一個字元:測試通過時列印一個句點;測試引發錯誤時列印一個E;測試導致斷言失敗時列印一個F。這就是你執行測試用例時,在輸出的第一行中看到的句點和字元數量各不相同的原因。如果測試用例包含很多單元測試,需要執行很長時間,就可通過觀察這些結果來獲悉有多少個測試通過了。
11-3 僱員:
編寫一個名為 Employee
的類,其方法__init__()
接受名、姓和年薪,並將它們都儲存在屬性中。編寫一個名為give_raise()
的方法,它預設將年薪增加 5000美元,但也能夠接受其他的年薪增加量。
為 Employee
編寫一個測試用例,其中包含兩個測試方法:test_give_default_raise()
和 test_give_custom_raise()
。使用方法 setUp()
,以免在每個測試方法中都建立新的僱員例項。執行這個測試用例,確認兩個測試都通過了。
class Employee():
def __init__(self,last_name, first_name, salary=10000 ):
self.last_name = last_name
self.first_name = first_name
self.salary = salary
def give_raise(self,added=5000):
self.salary += added
return added
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
import unittest
from EmployeeFile import Employee
class TestEmployeeRaise(unittest.TestCase):
def setUp(self):
self.test1 = Employee('Tom', 'Smith')
self.test2 = Employee('Tom', 'Smith',3000)
def test_give_default_raise(self):
self.salary1 = self.test1.give_raise()
self.assertEqual(str(self.salary1), '5000')
def test_give_custom_raise(self):
self.salary2 = self.test2.give_raise(3000)
self.assertEqual(str(self.salary2), '3000')
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
…
…
Ran 2 tests in 0.003s
OK
- 1
- 2
- 3
- 4
- 5
11.3 小結
測試是很多初學者都不熟悉的主題。作為初學者,並非必須為你嘗試的所有專案編寫測試;但參與工作量較大的專案時,你應對自己編寫的函式和類的重要行為進行測試。這樣你就能夠更加確定自己所做的工作不會破壞專案的其他部分,你就能夠隨心所欲地改進既有程式碼了。如果不小心破壞了原來的功能,你馬上就會知道,從而能夠輕鬆地修復問題。相比於等到不滿意的使用者報告bug後再採取措施,在測試未通過時採取措施要容易得多。 如果你在專案中包含了初步測試,其他程式設計師將更敬佩你,他們將能夠更得心應手地嘗試使用你編寫的程式碼,也更願意與你合作開發專案。如果你要跟其他程式設計師開發的專案共享程式碼,就必須證明你編寫的程式碼通過了既有測試,通常還需要為你新增的新行為編寫測試。 請通過多開展測試來熟悉程式碼測試過程。對於自己編寫的函式和類,請編寫針對其重要行為的測試,但在專案早期,不要試圖去編寫全覆蓋的測試用例,除非有充分的理由這樣做。