測試的意義 人們針對一個具體問題,通過分析和設計,最後用編程語言寫出了一個程式,如果它通過了語言解釋器(編譯器)的檢查,可以運行了,那麼下一步的工作就是設法確認它確實滿足了我們需求。這篇文章就是討論怎麼確認程式是否滿足用戶提出的需求。 滿足需求,換言之就是功能正常,確認功能正常可以從以下幾個方面確認 ...
測試的意義
人們針對一個具體問題,通過分析和設計,最後用編程語言寫出了一個程式,如果它通過了語言解釋器(編譯器)的檢查,可以運行了,那麼下一步的工作就是設法確認它確實滿足了我們需求。這篇文章就是討論怎麼確認程式是否滿足用戶提出的需求。
滿足需求,換言之就是功能正常,確認功能正常可以從以下幾個方面確認:
- 定義的函數對於所有正確的參數都能返回正確的結果
- 寫出的程式對所有合適的輸入都能產生正確的輸出
量化後的做法就是通過一系列的試運行,檢查程式的行為、輸入和輸出,如果檢查中發現了問題,就糾正、改進。這個也是功能測試和安全測試的初衷。
測試用例
測試考慮的基本問題就是怎麼運行程式,需要提供什麼數據,才能最大限度的檢查程式的各種行為和情況,最大可能的挖出程式中的錯誤和缺陷。基於設計什麼測試流程、提供什麼參數這種檢查程式運行的一套數據被稱為一個測試用例。一個測試用例就是可量化的測試流程。
確認測試用例又區分兩類方式:
黑盒測試
就是不看代碼,直接上手程式的使用測試。這裡不討論黑盒白盒測試
白盒測試的基礎是看程式的內部結構(代碼)和可能產生的執行路徑,根據內部結構來選擇測試的用例,使程式在試驗性運行中就能表示出儘可能多的不同行為。這個做法的基本理念就是:如果所有可能執行的路徑(順序、條件、while、for、嵌套...執行結構)都能給出正確的結果,那麼程式的正確性就能得到保證。
測試函數功能案例
各類的語言都會提供單元測試的庫,Python也不例外,python一般使用PyUnit(unittest)庫,unittest是Python自帶的單元測試框架,用於編寫和運行可重覆的測試,下麵介紹怎麼用unittest來測試函數的用法,我這裡只是簡單用了幾個測試方法,更多測試方法請查閱官網(https://docs.python.org/3/library/unittest.html)。
3個需要測試的函數:
def mysum(a, b):
return a + b
def mysubtraction(a, b):
return a - b
def is_evenNumbers(x):
if (x % 2) == 0:
return True
else:
return False
測試函數的方法:
import unittest
import testbox.mymath as mymath
class Test(unittest.TestCase):
def setUp(self):
print("The unit test function start.")
def test_mysum(self):
self.assertEqual(mymath.mysum(1, 2), 3, "mysum function have error!")
def test_mysubtraction(self):
self.assertEqual(mymath.mysubtraction(2, 1), 1, "mysubtraction function have error!")
def test_is_evenNumbers(self):
self.assertTrue(mymath.is_evenNumbers(2), "error")
def test_is_evenNumbers2(self):
self.assertFalse(mymath.is_evenNumbers(3), "error")
def tearDown(self):
print("The unit test end.")
if __name__ == '__main__':
unittest.main()
輸出:
Testing started at 12:26 PM ...
The unit test function start.
The unit test end.
The unit test function start.
The unit test end.
The unit test function start.
The unit test end.
The unit test function start.
The unit test end.
assert關鍵字的用法
功能其實和上面測試函數用法是一樣的,只不過assert可以直接使用在代碼里。這個關鍵字也比較生僻,也沒見什麼場景需要用它,也就這裡為了做個案例,我才用它寫了個demo。
def testasserts(a):
assert a == 2, Exception("parameter a not is 2, so have a error.")
if a == 2:
print("function run.")
print("OK. function end.")
if __name__ == '__main__':
testasserts(1)
print("Program is end.")
輸出:
Traceback (most recent call last):
File "/Users/Mysticbinary/Document/code/personage/python/TestPython1/testbox/testadd.py", line 9, in <module>
testasserts(1)
File "/Users/Mysticbinary/Document/code/personage/python/TestPython1/testbox/testadd.py", line 2, in testasserts
assert a == 2, Exception("parameter a not is 2, so have a error.")
AssertionError: parameter a not is 2, so have a error.
測試類功能案例
類功能的測試和函數測試一樣,只不過有一個竅門就是,測試、使用類的時候都需要先實例化類,而實例化類的操作,都可以放在setUp()裡面操作。
需要測試的類:
class Library:
allBook = ["php", "java"]
def __init__(self):
print("Library class create completion.")
def savebook(self, bookname):
self.allBook.append(bookname);
return self.allBook
def showbook(self):
print(self.allBook)
return self.allBook
測試類的方法:
import unittest
import testbox.myclass as myc
class TestClass(unittest.TestCase):
def setUp(self):
print("The unit test class start.")
self.library = myc.Library()
self.newbook = "python"
def test_savebook(self):
self.assertIn(self.newbook, self.library.savebook(self.newbook), "errow 1")
def test_showbook(self):
self.assertIn(self.newbook, self.library.showbook(), "errow 2")
def tearDown(self):
print("The unit test end.")
if __name__ == '__main__':
unittest.main()
輸出:
Testing started at 12:31 PM ...
The unit test class start.
Library class create completion.
The unit test end.
The unit test class start.
Library class create completion.
['php', 'java', 'python']
The unit test end.
安全測試案例-簡訊轟炸
我前面說過,功能測試和安全測試都有同樣的初衷,但是具體的測試手法兩者不太一樣,但是一些特定的場景下,使用單元測試的方法,也是能測試一些安全問題的,比如說測介面越權、簡訊介面重覆導致的簡訊轟炸問題等。我這裡只是拋磚引玉一下通過單元測試的手法來做安全測試的例子,但為做深入研究。
def send_message(phone):
keys = phones_dict.keys()
if phone not in keys:
phones_dict[phone] = 0
else:
if phones_dict[phone] < 10:
# 執行發簡訊的流程
phones_dict[phone] = (phones_dict[phone] + 1)
print("已經發送了{}次簡訊".format(phones_dict[phone]))
return "success"
else:
print("抱歉,該{}手機號 已經達到今天發簡訊的上限,請明天再來。".format(phone))
return "error"
測試發簡訊函數安全的測試用例:
def test_send_message(self):
result = list()
for i in range(0, 11):
result.append(sms.send_message("13193388105"))
print(result)
self.assertNotIn("error", result, "send_message have error.")
輸出:
Testing started at 9:48 PM ...
The unit test function start.
已經發送了1次簡訊
已經發送了2次簡訊
已經發送了3次簡訊
已經發送了4次簡訊
已經發送了5次簡訊
已經發送了6次簡訊
已經發送了7次簡訊
已經發送了8次簡訊
已經發送了9次簡訊
已經發送了10次簡訊
[None, 'success', 'success', 'success', 'success', 'success', 'success', 'success', 'success', 'success', 'success']
The unit test end.
__main__全局變數解釋
除了單元測試,設置模塊的運行入口(main)也是一種測試方式,就是針對每個模塊單獨的調用裡面的函數。__main__其實是一個全局變數,解釋器發現如果該模塊是被導入的,那麼__main__就會被賦值為這個模塊的名字,如果這個模塊是作為主模塊啟動時,那麼解釋器就會給__main__賦值為“main”字元串。
總結
- 建議不要在程式上線的時候還帶著assert、print之類的調試語句、避免信息泄露。
- 單元測試不單隻用於功能測試,也可以用在一些特定的安全測試里(具體範圍有那些,我沒有研究,如果有需求的話,我可能繼續深入研究)。
- unittest的斷言非常簡單易用,基本看一眼就能懂,但是懂斷言的用法,不代表你就會寫測試用例,能看到程式代碼的執行結構才是寫出測試的關鍵所在。一句話就是:寫斷言易,看代碼不易。