2020年9月22日星期二

011 Python编程 從入門到實踐 第一部分 基礎知識 第 11 章 測試代碼

 申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!



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()

 

 

 

 

 

 

 

 

函數get_formatted_name() 將名和姓合併成姓名,在名和姓之間加上一個空格,並將它們的首字母都大寫,再返回結果。為核實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 = 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 + '.')

 

 

 

 

 

 

 

 

這個程式從name_function.py中導入get_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

 

 

 

 

 

 

 

 

從上述輸出可知,合併得到的姓名正確無誤。現在假設我們要修改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 的類,並編寫一系列方法對函數行為的不同方面進行測試。

下面是一個隻包含一個方法的測試用例,它檢查函數get_formatted_name() 在給定名和姓時能否正確地工作:

test_name_function.py

 

  import unittest

  from name_function import get_formatted_name

 

class NamesTestCase(unittest.TestCase):

      """測試name_function.py"""

 

      def test_first_last_name(self):

          """能夠正確地處理像Janis Joplin這樣的姓名嗎?"""

         formatted_name = get_formatted_name('janis', 'joplin')

         self.assertEqual(formatted_name, 'Janis Joplin')

 

  unittest.main()

 

 

 

 

 

 

 

 

首先,我們導入了模組unittest 和要測試的函數get_formatted_name() 。在處,我們創建了一個名為NamesTestCase 的類,用於包含一系列針對get_formatted_name() 的單元測試。你可隨便給這個類命名,但最好讓它看起來與要測試的函數相關,並包含字樣Test。這個類必須繼承unittest.TestCase 類,這樣Python才知道如何運行你編寫的測試。

NamesTestCase 只包含一個方法,用於測試get_formatted_name() 的一個方面。我們將這個方法命名為test_first_last_name() ,因為我們要核實的是只有名和姓的姓名能否被正確地格式化。我們運行testname_function.py時,所有以test 打頭的方法都將自動運行。在這個方法中,我們調用了要測試的函數,並存儲了要測試的返回值。在這個示例中,我們使用實參'janis' 'joplin' 調用get_formatted_name() ,並將結果存儲到變數formatted_name 中(見

處,我們使用了unittest 類最有用的功能之一:一個斷言 方法。斷言方法用來核實得到的結果是否與期望的結果一致。在這裡,我們知道get_formatted_name() 應返回這樣的姓名,即名和姓的首字母為大寫,且它們之間有一個空格,因此我們期望formatted_name 的值為Janis Joplin 。為檢查是否確實如此,我們調用unittest 的方法assertEqual() ,並向它傳遞formatted_name 'Janis Joplin' 。代碼行self.assertEqual(formatted_name, 'Janis Joplin') 的意思是說:formatted_name 的值同字串'Janis Joplin' 進行比較,如果它們相等,就萬事大吉,如果它們不相等,跟我說一聲!

代碼行unittest.main() Python運行這個檔中的測試。運行test_name_function.py時,得到的輸出如下:

 

.

----------------------------------------------------------------------

Ran 1 test in 0.000s

 

OK

 

 

 

 

 

 

 

 

1行的句點表明有一個測試通過了。接下來的一行指出Python運行了一個測試,消耗的時間不到0.001秒。最後的OK 表明該測試用例中的所有單元測試都通過了。

上述輸出表明,給定包含名和姓的姓名時,函數get_formatted_name() 總是能正確地處理。修改get_formatted_name() 後,可再次運行這個測試用例。如果它通過了,我們就知道在給定Janis Joplin 這樣的姓名時,這個函數依然能夠正確地處理。

11.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()

 

 

 

 

 

 

 

 

這個版本應該能夠正確地處理包含中間名的姓名,但對其進行測試時,我們發現它再也不能正確地處理只有名和姓的姓名。這次運行程式test_name_function.py時,輸出如下:

 

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.000s

 

FAILED (errors=1)

 

 

 

 

 

 

 

 

其中包含的資訊很多,因為測試未通過時,需要讓你知道的事情可能有很多。第1行輸出只有一個字母E (見),它指出測試用例中有一個單元測試導致了錯誤。接下來,我們看到NamesTestCase 中的test_first_last_name() 導致了錯誤(見)。測試用例包含眾多單元測試時,知道哪個測試未通過至關重要。在處,我們看到了一個標準的traceback,它指出函式呼叫get_formatted_name('janis', 'joplin') 有問題,因為它缺少一個必不可少的位置實參。

我們還看到運行了一個單元測試(見)。最後,還看到了一條消息,它指出整個測試用例都未通過,因為運行該測試用例時發生了一個錯誤(見)。這條消息位於輸出末尾,讓你一眼就能看到——你可不希望為獲悉有多少測試未通過而翻閱長長的輸出。

11.1.4 測試未通過時怎麼辦

測試未通過時怎麼辦呢?如果你檢查的條件沒錯,測試通過了意味著函數的行為是對的,而測試未通過意味著你編寫的新代碼有錯。因此,測試未通過時,不要修改測試,而應修復導致測試不能通過的代碼:檢查剛對函數所做的修改,找出導致函數行為不符合預期的修改。

在這個示例中,get_formatted_name() 以前只需要兩個實參——名和姓,但現在它要求提供名、中間名和姓。新增的中間名參數是必不可少的,這導致get_formatted_name() 的行為不符合預期。就這裡而言,最佳的選擇是讓中間名變為可選的。這樣做後,使用類似於Janis Joplin的姓名進行測試時,測試就會通過了,同時這個函數還能接受中間名。下面來修改get_formatted_name() ,將中間名設置為可選的,然後再次運行這個測試用例。如果通過了,我們接著確認這個函數能夠妥善地處理中間名。

要將中間名設置為可選的,可在函式定義中將形參middle 移到形參列表末尾,並將其預設值指定為一個空字串。我們還要添加一個if 測試,以便根據是否提供了中間名相應地創建姓名:

name_function.py

 

def get_formatted_name(first, last, middle=''):

    """生成整潔的姓名"""

    if middle:

        full_name = first + ' ' + middle + ' ' + last

    else:

        full_name = first + ' ' + last

    return full_name.title()

 

 

 

 

 

 

 

 

get_formatted_name() 的這個新版本中,中間名是可選的。如果向這個函數傳遞了中間名(if middle: ),姓名將包含名、中間名和姓,否則姓名將只包含名和姓。現在,對於兩種不同的姓名,這個函數都應該能夠正確地處理。為確定這個函數依然能夠正確地處理像Janis Joplin這樣的姓名,我們再次運行test_name_function.py

 

.

----------------------------------------------------------------------

Ran 1 test in 0.000s

 

OK

 

 

 

 

 

 

 

 

現在,測試用例通過了。太好了,這意味著這個函數又能正確地處理像Janis Joplin這樣的姓名了,而且我們無需手工測試這個函數。這個函數很容易就修復了,因為未通過的測試讓我們得知新代碼破壞了函數原來的行為。

11.1.5 添加新測試

確定get_formatted_name() 又能正確地處理簡單的姓名後,我們再編寫一個測試,用於測試包含中間名的姓名。為此,我們在NamesTestCase 類中再添加一個方法:

 

  import unittest

  from name_function import get_formatted_name

 

  class NamesTestCase(unittest.TestCase):

      """測試name_function.py """

 

      def test_first_last_name(self):

          """能夠正確地處理像Janis Joplin這樣的姓名嗎?"""

          formatted_name = get_formatted_name('janis', 'joplin')

          self.assertEqual(formatted_name, 'Janis Joplin')

 

      def test_first_last_middle_name(self):

          """能夠正確地處理像Wolfgang Amadeus Mozart這樣的姓名嗎?"""

         formatted_name = get_formatted_name(

              'wolfgang', 'mozart', 'amadeus')

          self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')

 

  unittest.main()

 

 

 

 

 

 

 

 

我們將這個方法命名為test_first_last_middle_name() 。方法名必須以test_打頭,這樣它才會在我們運行test_name_function.py時自動運行。這個方法名清楚地指出了它測試的是get_formatted_name() 的哪個行為,這樣,如果該測試未通過,我們就會馬上知道受影響的是哪種類型的姓名。在TestCase 類中使用很長的方法名是可以的;這些方法的名稱必須是描述性的,這才能讓你明白測試未通過時的輸出;這些方法由Python自動調用,你根本不用編寫調用它們的代碼。

為測試函數get_formatted_name() ,我們使用名、姓和中間名調用它(見),再使用assertEqual() 檢查返回的姓名是否與預期的姓名(名、中間名和姓)一致。我們再次運行test_name_function.py時,兩個測試都通過了:

 

..

----------------------------------------------------------------------

Ran 2 tests in 0.000s

 

OK

 

 

 

 

 

 

 

 

太好了!現在我們知道,這個函數又能正確地處理像Janis Joplin這樣的姓名了,我們還深信它也能夠正確地處理像Wolfgang Amadeus Mozart這樣的姓名。

動手試一試

11-1 城市和國家 :編寫一個函數,它接受兩個形參:一個城市名和一個國家名。這個函數返回一個格式為City, Country 的字串,如Santiago, Chile 。將這個函數存儲在一個名為city_functions.py的模組中。

創建一個名為test_cities.py的程式,對剛編寫的函數進行測試(別忘了,你需要導入模組unittest 以及要測試的函數)。編寫一個名為test_city_country() 的方法,核實使用類似於'santiago' 'chile' 這樣的值來調用前述函數時,得到的字串是正確的。運行test_cities.py ,確認測試test_city_country() 通過了。

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() 通過了。

11.2 測試類

在本章前半部分,你編寫了針對單個函數的測試,下面來編寫針對類的測試。很多程式中都會用到類,因此能夠證明你的類能夠正確地工作會大有裨益。如果針對類的測試通過了,你就能確信對類所做的改進沒有意外地破壞其原有的行為。

11.2.1 各種斷言方法

Pythonunittest.TestCase 類中提供了很多斷言方法。前面說過,斷言方法檢查你認為應該滿足的條件是否確實滿足。如果該條件確實滿足,你對程式列為的假設就得到了確認,你就可以確信其中沒有錯誤。如果你認為應該滿足的條件實際上並不滿足,Python將引發異常。

11-1描述了6個常用的斷言方法。使用這些方法可核實返回的值等於或不等於預期的值、返回的值為True False 、返回的值在列表中或不在列表中。你只能在繼承unittest.TestCase 的類中使用這些方法,下面來看看如何在測試類時使用其中的一個。

11-1 unittest Module中的斷言方法

方法

用途

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(question)

 

     def store_response(self, new_response):

          """存儲單份調查答卷"""

          self.responses.append(new_response)

 

     def show_results(self):

          """顯示收集到的所有答卷"""

          print("Survey results:")

          for response in responses:

              print('- ' + response)

 

 

 

 

 

 

 

 

這個類首先存儲了一個你指定的調查問題(見),並創建了一個空清單,用於存儲答案。這個類包含列印調查問題的方法(見)、在答案列表中添加新答案的方法()以及將存儲在清單中的答案都列印出來的方法(見)。要創建這個類的實例,只需提供一個問題即可。有了表示調查的實例後,就可使用show_question() 來顯示其中的問題,可使用store_response() 來存儲答案,並使用show_results() 來顯示調查結果。

為證明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 = 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? " ),並使用這個問題創建了一個AnonymousSurvey 物件。接下來,這個程式調用show_question() 來顯示問題,並提示使用者輸入答案。收到每個答案的同時將其存儲起來。用戶輸入所有答案(輸入q 要求退出)後,調用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

 

 

 

 

 

 

 

 

AnonymousSurvey 類可用於進行簡單的匿名調查。假設我們將它放在了模組survey 中,並想進行改進:讓每位用戶都可輸入多個答案;編寫一個方法,它只列出不同的答案,並指出每個答案出現了多少次;再編寫一個類,用於管理非匿名調查。

進行上述修改存在風險,可能會影響AnonymousSurvey 類的當前行為。例如,允許每位用戶輸入多個答案時,可能不小心修改了處理單個答案的方式。要確認在開發這個模組時沒有破壞既有行為,可以編寫針對這個類的測試。

11.2.3 測試AnonymousSurvey 

下面來編寫一個測試,對AnonymousSurvey 類的行為的一個方面進行驗證:如果用戶面對調查問題時只提供了一個答案,這個答案也能被妥善地存儲。為此,我們將在這個答案被存儲後,使用方法assertIn() 來核實它包含在答案列表中:

test_survey.py

 

  import unittest

  from survey import AnonymousSurvey

 

class TestAnonmyousSurvey(unittest.TestCase):

      """針對AnonymousSurvey類的測試"""

 

     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()

 

 

 

 

 

 

 

 

我們首先導入了模組unittest 以及要測試的類AnonymousSurvey 。我們將測試用例命名為TestAnonymousSurvey ,它也繼承了unittest.TestCase (見)。第一個測試方法驗證調查問題的單個答案被存儲後,會包含在調查結果列表中。對於這個方法,一個不錯的描述性名稱是test_store_single_response() (見)。如果這個測試未通過,我們就能通過輸出中的方法名得知,在存儲單個調查答案方面存在問題。

要測試類的行為,需要創建其實例。在處,我們使用問題"What language did you first learn to speak?" 創建了一個名為my_survey 的實例,然後使用方法store_response() 存儲了單個答案English 。接下來,我們檢查English 是否包含在列表my_survey.responses 中,以核實這個答案是否被妥善地存儲了(見

當我們運行test_survey.py時,測試通過了:

 

.

----------------------------------------------------------------------

Ran 1 test in 0.001s

 

OK

 

 

 

 

 

 

 

 

這很好,但只能收集一個答案的調查用途不大。下面來核實用戶提供三個答案時,它們也將被妥善地存儲。為此,我們在TestAnonymousSurvey 中再添加一個方法:

 

  import unittest

  from survey import AnonymousSurvey

 

  class TestAnonymousSurvey(unittest.TestCase):

      """針對AnonymousSurvey類的測試"""

 

      def test_store_single_response(self):

          """測試單個答案會被妥善地存儲"""

          --snip--

 

      def test_store_three_responses(self):

          """測試三個答案會被妥善地存儲"""

          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)

 

  unittest.main()

 

 

 

 

 

 

 

 

我們將這個方法命名為test_store_three_responses() ,並像test_store_single_response() 一樣,在其中創建一個調查物件。我們定義了一個包含三個不同答案的列表(見),再對其中每個答案都調用store_response() 。存儲這些答案後,我們使用一個迴圈來確認每個答案都包含在my_survey.responses 中(見

我們再次運行test_survey.py時,兩個測試(針對單個答案的測試和針對三個答案的測試)都通過了:

 

..

----------------------------------------------------------------------

Ran 2 tests in 0.000s

 

OK

 

 

 

 

 

 

 

 

前述做法的效果很好,但這些測試有些重複的地方。下面使用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):

      """針對AnonymousSurvey類的測試"""

 

      def setUp(self):

          """

          創建一個調查物件和一組答案,供使用的測試方法使用

          """

          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):

          """測試單個答案會被妥善地存儲"""

          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() 做了兩件事情:創建一個調查物件(見);創建一個答案列表(見)。存儲這兩樣東西的變數名包含首碼self (即存儲在屬性中),因此可在這個類的任何地方使用。這讓兩個測試方法都更簡單,因為它們都不用創建調查物件和答案。方法test_store_three_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() ,以免在每個測試方法中都創建新的雇員實例。運行這個測試用例,確認兩個測試都通過了。

11.3 小結

在本章中,你學習了:如何使用模組unittest 中的工具來為函數和類編寫測試;如何編寫繼承unittest.TestCase 的類,以及如何編寫測試方法,以核實函數和類的行為符合預期;如何使用方法setUp() 來根據類高效地創建實例並設置其屬性,以便在類的所有測試方法中都可使用它們。

測試是很多初學者都不熟悉的主題。作為初學者,並非必須為你嘗試的所有項目編寫測試;但參與工作量較大的項目時,你應對自己編寫的函數和類的重要行為進行測試。這樣你就能夠更加確定自己所做的工作不會破壞專案的其他部分,你就能夠隨心所欲地改進既有代碼了。如果不小心破壞了原來的功能,你馬上就會知道,從而能夠輕鬆地修復問題。相比于等到不滿意的用戶報告bug後再採取措施,在測試未通過時採取措施要容易得多。

如果你在項目中包含了初步測試,其他程式師將更敬佩你,他們將能夠更得心應手地嘗試使用你編寫的代碼,也更願意與你合作開發項目。如果你要跟其他程式師開發的專案共用代碼,就必須證明你編寫的代碼通過了既有測試,通常還需要為你添加的新行為編寫測試。

請通過多開展測試來熟悉代碼測試過程。對於自己編寫的函數和類,請編寫針對其重要行為的測試,但在項目早期,不要試圖去編寫全覆蓋的測試用例,除非有充分的理由這樣做。



大澳飛宇註:

本書入門精華已為您連載完畢,謝謝閱讀

本書非常值得您去購買來學、來據以實踐。為了尊重版權,本人僅能連載到這讓您學習,其餘課文真要學習的話,請直接向相關網站購買正版。謝謝!

本書是一本針對所有層次的Python讀者而作的Python入門書。全書分兩部分:首部分介紹用Python 編程所必須了解的基本概念,包括matplotlib、NumPy和Pygal等強大的Python庫和工具介紹,以及列表、字典、if語句、類、文件與異常、代碼測試等內容;第二部分將理論付諸實踐,講解如何開發三個項目,包括簡單的Python 2D游戲開發,如何利用數據生成交互式的信息圖,以及創建和定制簡單的Web應用,並幫讀者解決常見編程問題和困惑。




0 留言:

發佈留言