python學習筆記(十八)測試函數

python學習筆記(十八)測試函數

1、測試初見
每一個具有實用意義的程序都需要編寫函數或者類,但是從主觀角度,我們往往難以判斷這些函數或者類面對不同的輸入,是否都能夠按要求進行操作。
幸運的是,python中的模塊unittest爲我們提供了相應的測試工具,我們可以根據這些工具編寫測試代碼,在用戶發現問題之前就把它們測試出來。這裏我們簡單瞭解一下有關測試的幾個概念:
a. 單元測試:用於覈實函數的某個方面沒有問題,注意這裏的“某個方面”,編寫單元測試最主要的地方就在於“不要貪”。
b. 測試用例:一組單元測試。測試用例的編寫原則應當是綜合考慮函數可能收到的各種輸入。
c. 全覆蓋式測試:包含一整套單元測試。全覆蓋式測試的編寫原則應當是包含各種可能的函數使用方式。

2、測試通過
下面我們根據例子來簡單的學習一下測試代碼的編寫,首先,創建一個空文件夾day_18。
在這裏插入圖片描述

下面在文件 name_function.py 中編寫待測試函數get_formatted_name(),並將其存放在文件夾day_18中。

def get_formatted_name(first, last):
	"""將傳入的名和姓合併成完整姓名,並在名和姓之間加上一個空格,將其首字母大寫,最終將結果返回"""
	full_name = first + ' ' + last
	return full_name.title()

接下來我們重新創建一個 test_name_function.py 文件(存放在day_18中),編寫測試代碼。

import unittest
from name_function import get_formatted_name

class NameTestCase(unittest.TestCase):
	"""測試name_function.py"""

	def test_first_last_name(self):
		"""測試能否正確處理樣例姓名"""
		formatted_name = get_formatted_name('python', 'java')
		self.assertEqual(formatted_name, 'Python Java')

unittest.main()

我們先來看代碼,編寫測試代碼文件一般有一些固定的小套路:
a. 導入模塊unittest,因爲我們在編寫測試類的時候需要繼承模塊unittest中的類(主要是TestCase,爲了告訴python如何運行我們編寫的測試)。
b. 導入待測試函數。
c. 創建測試類(這個類的命名是隨意的,但是最好聯繫待測試函數,幷包含"Test"),繼承unittest.TestCase,編寫一系列方法測試函數行爲的不同方面(注意不重不漏)。這裏注意,當我們運行測試代碼文件時,所有以 test_ 開頭的方法都將自動運行。
d. 測試文件的最後一般都是 unittest.main() ,它讓python運行這個文件中的測試。

上述代碼只自定義了一個方法,因此它只用於測試待測試函數的一個方面(只有名和姓的輸入能否被正確地操作)。這個方法中最重要的就是我們使用了unittest類最有用的功能之一——斷言方法(用來覈實得到的結果是否與期望值一致)。接下來我們來看運行結果:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

第一行的句點表示有一個測試通過了,然後指出python運行一個測試所消耗的時間,最後的OK表明該測試用例中的所有單元測試都通過了。

3、測試未通過
下面我們改寫 name_function.py 文件中的 get_formatted_name() 函數,看一下測試未通過的運行結果。

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__.NameTestCase)
測試能否正確處理樣例姓名
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/(老規矩,文件路徑手動打碼)/day_18/test_name_function.py", line 9, in test_first_last_name
    formatted_name = get_formatted_name('python', 'java')
TypeError: get_formatted_name() missing 1 required positional argument: 'last'

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

第一行的E指出測試用例中有一個單元測試導致了錯誤,然後具體指出是哪一個單元測試。後面就是我們在學習異常時的老朋友——traceback,異常報告指出引發異常的原因。“Ran 1 test in 0.000s”還是python運行一個測試所耗的時間,最後一行的“FAILED (errors=1)”則表明整個測試用例都未通過,因爲運行該測試用例時發生了一個錯誤。
測試未通過就說明測試的那部分代碼是錯誤的,這時候千萬不要想着去修改測試,而是根據錯誤報告調試不能通過測試的代碼,現在我們來修正 get_formatted_function() 函數。

def get_formatted_name(first, last, middle=''):
	"""生成完整的名字"""
	if middle:
		full_name = first + ' ' + middle + ' ' + last
	else:
		full_name = first + ' ' + last
	return full_name.title()

這裏我們不要想着把函數改回第二點中的例子,因爲中間名是常態,我們應該處理好這個問題,這裏使用的方法是運用默認值將middle形參變爲可選的。保存修改,重新運行 test_name_function.py 文件,運行結果爲:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

測試用例通過,函數編寫成功!

4、添加新測試
一個測試類一般都不止一個方法,因爲我們我們需要綜合考慮函數的所有輸入情況,下面我們來完善一下test_name_function.py 文件:

import unittest
from name_function import get_formatted_name

class NameTestCase(unittest.TestCase):
	"""測試name_function.py"""

	def test_first_last_name(self):
		"""測試能否正確處理樣例姓名"""
		formatted_name = get_formatted_name('python', 'java')
		self.assertEqual(formatted_name, 'Python Java')

	def test_first_last_middle_name(self):
		formatted_name = get_formatted_name('python', 'java', 'ruby')
		self.assertEqual(formatted_name, 'Python Ruby Java')

unittest.main()

注意新增方法也要以 test_ 開頭(否則無法在運行文件時自動運行),運行結果爲:

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

測試通過,函數編寫成功!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章