淺談如何測試Python代碼
一、介紹
編寫測試檢驗應用程序所有不同的功能。每一個測試集中在一個關注點上驗證結(jié)果是不是期望的。定期執(zhí)行測試確保應用程序按預期的工作。當測試覆蓋很大的時候,通過運行測試你就有自信確保修改點和新增點不會影響應用程序。
知識點
- 單元測試概念
- 使用 unittest 模塊
- 測試用例的編寫
- 異常測試
- 測試覆蓋率概念
- 使用 coverage 模塊
二、測試范圍
如果可能的話,代碼庫中的所有代碼都要測試。但這取決于開發(fā)者,如果寫一個健壯性測試是不切實際的,你可以跳過它。就像 _Nick Coghlan_(Python 核心開發(fā)成員) 在訪談里面說的:有一個堅實可靠的測試套件,你可以做出大的改動,并確信外部可見行為保持不變。
三、單元測試
這里引用維基百科的介紹:
在計算機編程中,單元測試(英語:Unit Testing)又稱為模塊測試, 是針對程序模塊(軟件設計的最小單位)來進行正確性檢驗的測試工作。程序單元是應用的最小可測試部件。在過程化編程中,一個單元就是單個程序、函數(shù)、過程等;對于面向?qū)ο缶幊?,最小單元就是方法,包括基類(超類)、抽象類、或者派生類(子類)中的方法?/p>
單元測試模塊
在 Python 里我們有 unittest 這個模塊來幫助我們進行單元測試。
階乘計算程序
在這個例子中我們將寫一個計算階乘的程序 factorial.py:
import sys def fact(n): """ 階乘函數(shù) :arg n: 數(shù)字 :returns: n 的階乘 """ if n == 0: return 1 return n * fact(n -1) def div(n): """ 只是做除法 """ res = 10 / n return res def main(n): res = fact(n) print(res) if __name__ == '__main__': if len(sys.argv) > 1: main(int(sys.argv[1]))
運行程序:
$ python3 factorial.py 5
四、第一個測試用例
測試哪個函數(shù)?
正如你所看到的, fact(n) 這個函數(shù)執(zhí)行所有的計算,所以我們至少應該測試這個函數(shù)。
編輯 factorial_test.py 文件,代碼如下:
import unittest from factorial import fact class TestFactorial(unittest.TestCase): """ 我們的基本測試類 """ def test_fact(self): """ 實際測試 任何以 `test_` 開頭的方法都被視作測試用例 """ res = fact(5) self.assertEqual(res, 120) if __name__ == '__main__': unittest.main()
運行測試:
$ python3 factorial_test.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
說明
我們首先導入了 unittest 模塊,然后測試我們需要測試的函數(shù)。
測試用例是通過子類化 unittest.TestCase 創(chuàng)建的。
現(xiàn)在我們打開測試文件并且把 120 更改為 121,然后看看會發(fā)生什么?
各類 assert 語句
| Method | Checks that | New in |
| assertEqual(a, b) | a == b |
|
| assertNotEqual(a, b) | a != b |
|
| assertTrue(x) | bool(x) is True |
|
| assertFalse(x) | bool(x) is False |
|
| assertIs(a, b) | a is b |
2.7 |
| assertIsNot(a, b) | a is not b |
2.7 |
| assertIsNone(x) | x is None |
2.7 |
| assertIsNotNone(x) | x is not None |
2.7 |
| assertIn(a, b) | a in b |
2.7 |
| assertNotIn(a, b) | a not in b |
2.7 |
| assertIsInstance(a, b) | isinstance(a, b) |
2.7 |
| assertNotIsInstance(a, b) | not isinstance(a, b) |
2.7 |
五、異常測試
如果我們在 factorial.py 中調(diào)用 div(0),我們能看到異常被拋出。
我們也能測試這些異常,就像這樣:
self.assertRaises(ZeroDivisionError, div, 0)
完整代碼:
import unittest from factorial import fact, div class TestFactorial(unittest.TestCase): """ 我們的基本測試類 """ def test_fact(self): """ 實際測試 任何以 `test_` 開頭的方法都被視作測試用例 """ res = fact(5) self.assertEqual(res, 120) def test_error(self): """ 測試由運行時錯誤引發(fā)的異常 """ self.assertRaises(ZeroDivisionError, div, 0) if __name__ == '__main__': unittest.main()
六、mounttab.py
mounttab.py 中只有一個 mount_details() 函數(shù),函數(shù)分析并打印掛載詳細信息。
import os
def mount_details():
"""
打印掛載詳細信息
"""
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
print('{} on {} type {}'.format(words[0],words[1],words[2]), end=' ')
if len(words) > 5:
print('({})'.format(' '.join(words[3:-2])))
else:
print()
fd.close()
if __name__ == '__main__':
mount_details()
重構(gòu) mounttab.py
現(xiàn)在我們在 mounttab2.py 中重構(gòu)了上面的代碼并且有一個我們能容易的測試的新函數(shù) parse_mounts()。
import os
def parse_mounts():
"""
分析 /proc/mounts 并 返回元祖的列表
"""
result = []
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
if len(words) > 5:
res = (words[0],words[1],words[2],'({})'.format(' '.join(words[3:-2])))
else:
res = (words[0],words[1],words[2])
result.append(res)
fd.close()
return result
def mount_details():
"""
打印掛載詳細信息
"""
result = parse_mounts()
for line in result:
if len(line) == 4:
print('{} on {} type {} {}'.format(*line))
else:
print('{} on {} type {}'.format(*line))
if __name__ == '__main__':
mount_details()
同樣我們測試代碼,編寫 mounttest.py 文件:
#!/usr/bin/env python import unittest from mounttab2 import parse_mounts class TestMount(unittest.TestCase): """ 我們的基本測試類 """ def test_parsemount(self): """ 實際測試 任何以 `test_` 開頭的方法都被視作測試用例 """ result = parse_mounts() self.assertIsInstance(result, list) self.assertIsInstance(result[0], tuple) def test_rootext4(self): """ 測試找出根文件系統(tǒng) """ result = parse_mounts() for line in result: if line[1] == '/' and line[2] != 'rootfs': self.assertEqual(line[2], 'ext4') if __name__ == '__main__': unittest.main()
運行程序
$ python3 mounttest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
七、測試覆蓋率
測試覆蓋率是找到代碼庫未經(jīng)測試的部分的簡單方法。它并不會告訴你的測試好不好。
在 Python 中我們已經(jīng)有了一個不錯的覆蓋率工具來幫助我們。你可以在實驗樓環(huán)境中安裝它:
$ sudo pip3 install coverage
覆蓋率示例
$ coverage3 run mounttest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.013s
OK
$ coverage3 report -m
Name Stmts Miss Cover Missing
--------------------------------------------
mounttab2.py 22 7 68% 16, 25-30, 34
mounttest.py 14 0 100%
--------------------------------------------
TOTAL 36 7 81%
我們還可以使用下面的命令以 HTML 文件的形式輸出覆蓋率結(jié)果,然后在瀏覽器中查看它。
$ coverage3 html

八、總結(jié)
知識點回顧:
- 單元測試概念
- 使用 unittest 模塊
- 測試用例的編寫
- 異常測試
- 測試覆蓋率概念
- 使用 coverage 模塊
本節(jié)了解了什么是單元測試,unittest 模塊怎么用,測試用例怎么寫。以及最后我們使用第三方模塊 coverage 進行了覆蓋率測試。
在實際生產(chǎn)環(huán)境中,測試環(huán)節(jié)是非常重要的的一環(huán),即便志不在測試工程師,但以后的趨勢就是 DevOps,所以掌握良好的測試技能也是很有用的。
到此這篇關于淺談如何測試Python代碼的文章就介紹到這了,更多相關測試Python代碼內(nèi)容請搜索本站以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持本站!
版權聲明:本站文章來源標注為YINGSOO的內(nèi)容版權均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非maisonbaluchon.cn所屬的服務器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權,請聯(lián)系alex-e#qq.com處理。
關注官方微信