淺談如何測(cè)試Python代碼
一、介紹
編寫(xiě)測(cè)試檢驗(yàn)應(yīng)用程序所有不同的功能。每一個(gè)測(cè)試集中在一個(gè)關(guān)注點(diǎn)上驗(yàn)證結(jié)果是不是期望的。定期執(zhí)行測(cè)試確保應(yīng)用程序按預(yù)期的工作。當(dāng)測(cè)試覆蓋很大的時(shí)候,通過(guò)運(yùn)行測(cè)試你就有自信確保修改點(diǎn)和新增點(diǎn)不會(huì)影響應(yīng)用程序。
知識(shí)點(diǎn)
- 單元測(cè)試概念
- 使用 unittest 模塊
- 測(cè)試用例的編寫(xiě)
- 異常測(cè)試
- 測(cè)試覆蓋率概念
- 使用 coverage 模塊
二、測(cè)試范圍
如果可能的話,代碼庫(kù)中的所有代碼都要測(cè)試。但這取決于開(kāi)發(fā)者,如果寫(xiě)一個(gè)健壯性測(cè)試是不切實(shí)際的,你可以跳過(guò)它。就像 _Nick Coghlan_(Python 核心開(kāi)發(fā)成員) 在訪談里面說(shuō)的:有一個(gè)堅(jiān)實(shí)可靠的測(cè)試套件,你可以做出大的改動(dòng),并確信外部可見(jiàn)行為保持不變。
三、單元測(cè)試
這里引用維基百科的介紹:
在計(jì)算機(jī)編程中,單元測(cè)試(英語(yǔ):Unit Testing)又稱為模塊測(cè)試, 是針對(duì)程序模塊(軟件設(shè)計(jì)的最小單位)來(lái)進(jìn)行正確性檢驗(yàn)的測(cè)試工作。程序單元是應(yīng)用的最小可測(cè)試部件。在過(guò)程化編程中,一個(gè)單元就是單個(gè)程序、函數(shù)、過(guò)程等;對(duì)于面向?qū)ο缶幊?,最小單元就是方法,包括基類(超類)、抽象類、或者派生類(子類)中的方法?/p>
單元測(cè)試模塊
在 Python 里我們有 unittest 這個(gè)模塊來(lái)幫助我們進(jìn)行單元測(cè)試。
階乘計(jì)算程序
在這個(gè)例子中我們將寫(xiě)一個(gè)計(jì)算階乘的程序 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]))
運(yùn)行程序:
$ python3 factorial.py 5
四、第一個(gè)測(cè)試用例
測(cè)試哪個(gè)函數(shù)?
正如你所看到的, fact(n) 這個(gè)函數(shù)執(zhí)行所有的計(jì)算,所以我們至少應(yīng)該測(cè)試這個(gè)函數(shù)。
編輯 factorial_test.py 文件,代碼如下:
import unittest
from factorial import fact
class TestFactorial(unittest.TestCase):
"""
我們的基本測(cè)試類
"""
def test_fact(self):
"""
實(shí)際測(cè)試
任何以 `test_` 開(kāi)頭的方法都被視作測(cè)試用例
"""
res = fact(5)
self.assertEqual(res, 120)
if __name__ == '__main__':
unittest.main()
運(yùn)行測(cè)試:
$ python3 factorial_test.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
說(shuō)明
我們首先導(dǎo)入了 unittest 模塊,然后測(cè)試我們需要測(cè)試的函數(shù)。
測(cè)試用例是通過(guò)子類化 unittest.TestCase 創(chuàng)建的。
現(xiàn)在我們打開(kāi)測(cè)試文件并且把 120 更改為 121,然后看看會(huì)發(fā)生什么?
各類 assert 語(yǔ)句
| 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 |
五、異常測(cè)試
如果我們?cè)?factorial.py 中調(diào)用 div(0),我們能看到異常被拋出。
我們也能測(cè)試這些異常,就像這樣:
self.assertRaises(ZeroDivisionError, div, 0)
完整代碼:
import unittest
from factorial import fact, div
class TestFactorial(unittest.TestCase):
"""
我們的基本測(cè)試類
"""
def test_fact(self):
"""
實(shí)際測(cè)試
任何以 `test_` 開(kāi)頭的方法都被視作測(cè)試用例
"""
res = fact(5)
self.assertEqual(res, 120)
def test_error(self):
"""
測(cè)試由運(yùn)行時(shí)錯(cuò)誤引發(fā)的異常
"""
self.assertRaises(ZeroDivisionError, div, 0)
if __name__ == '__main__':
unittest.main()
六、mounttab.py
mounttab.py 中只有一個(gè) mount_details() 函數(shù),函數(shù)分析并打印掛載詳細(xì)信息。
import os
def mount_details():
"""
打印掛載詳細(xì)信息
"""
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)在我們?cè)?mounttab2.py 中重構(gòu)了上面的代碼并且有一個(gè)我們能容易的測(cè)試的新函數(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():
"""
打印掛載詳細(xì)信息
"""
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()
同樣我們測(cè)試代碼,編寫(xiě) mounttest.py 文件:
#!/usr/bin/env python
import unittest
from mounttab2 import parse_mounts
class TestMount(unittest.TestCase):
"""
我們的基本測(cè)試類
"""
def test_parsemount(self):
"""
實(shí)際測(cè)試
任何以 `test_` 開(kāi)頭的方法都被視作測(cè)試用例
"""
result = parse_mounts()
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], tuple)
def test_rootext4(self):
"""
測(cè)試找出根文件系統(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()
運(yùn)行程序
$ python3 mounttest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
七、測(cè)試覆蓋率
測(cè)試覆蓋率是找到代碼庫(kù)未經(jīng)測(cè)試的部分的簡(jiǎn)單方法。它并不會(huì)告訴你的測(cè)試好不好。
在 Python 中我們已經(jīng)有了一個(gè)不錯(cuò)的覆蓋率工具來(lái)幫助我們。你可以在實(shí)驗(yàn)樓環(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é)
知識(shí)點(diǎn)回顧:
- 單元測(cè)試概念
- 使用 unittest 模塊
- 測(cè)試用例的編寫(xiě)
- 異常測(cè)試
- 測(cè)試覆蓋率概念
- 使用 coverage 模塊
本節(jié)了解了什么是單元測(cè)試,unittest 模塊怎么用,測(cè)試用例怎么寫(xiě)。以及最后我們使用第三方模塊 coverage 進(jìn)行了覆蓋率測(cè)試。
在實(shí)際生產(chǎn)環(huán)境中,測(cè)試環(huán)節(jié)是非常重要的的一環(huán),即便志不在測(cè)試工程師,但以后的趨勢(shì)就是 DevOps,所以掌握良好的測(cè)試技能也是很有用的。
到此這篇關(guān)于淺談如何測(cè)試Python代碼的文章就介紹到這了,更多相關(guān)測(cè)試Python代碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python實(shí)現(xiàn)http接口自動(dòng)化測(cè)試的示例代碼
- Python requests接口測(cè)試實(shí)現(xiàn)代碼
- Python reques接口測(cè)試框架實(shí)現(xiàn)代碼
- Python分類測(cè)試代碼實(shí)例匯總
- Python3 webservice接口測(cè)試代碼詳解
- Python+appium框架原生代碼實(shí)現(xiàn)App自動(dòng)化測(cè)試詳解
- python英語(yǔ)單詞測(cè)試小程序代碼實(shí)例
- python自動(dòng)化測(cè)試之DDT數(shù)據(jù)驅(qū)動(dòng)的實(shí)現(xiàn)代碼
- Python代碼縮進(jìn)和測(cè)試模塊示例詳解
相關(guān)文章
Python中用于檢查英文字母大寫(xiě)的isupper()方法
這篇文章主要介紹了Python中用于檢查英文字母大寫(xiě)的isupper()方法,是Python入門中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-05-05
解決pycharm修改代碼后第一次運(yùn)行不生效的問(wèn)題
這篇文章主要介紹了解決pycharm修改代碼后第一次運(yùn)行不生效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
一文帶你了解Python中不同數(shù)據(jù)對(duì)象的空值校驗(yàn)方法
空值校驗(yàn)在數(shù)據(jù)處理和應(yīng)用程序開(kāi)發(fā)中是一個(gè)非常重要的任務(wù),Python提供了多種方式來(lái)檢查不同數(shù)據(jù)對(duì)象(如字符串、列表、字典、集合等)是否為空或包含空值,下面就跟隨小編一起來(lái)學(xué)習(xí)一下吧2024-01-01
Python3模擬curl發(fā)送post請(qǐng)求操作示例
這篇文章主要介紹了Python3模擬curl發(fā)送post請(qǐng)求操作,結(jié)合實(shí)例形式分析了Python3使用Request請(qǐng)求模擬curl發(fā)送post相關(guān)操作技巧,需要的朋友可以參考下2019-05-05
Python模仿POST提交HTTP數(shù)據(jù)及使用Cookie值的方法
這篇文章主要介紹了Python模仿POST提交HTTP數(shù)據(jù)及使用Cookie值的方法,通過(guò)兩種不同的實(shí)現(xiàn)方法較為詳細(xì)的講述了HTTP數(shù)據(jù)通信及cookie的具體用法,需要的朋友可以參考下2014-11-11
Python中自定義函方法與參數(shù)具有默認(rèn)值的函數(shù)
這篇文章主要介紹了Python中自定義函方法與參數(shù)具有默認(rèn)值的函數(shù),在Python編程中,可以使用已經(jīng)定義好的函數(shù),也可以自定義函數(shù)實(shí)現(xiàn)某些特殊的功能,更多相關(guān)資料,請(qǐng)需要的人參考下面文章內(nèi)容2022-02-02
python3+PyQt5實(shí)現(xiàn)使用剪貼板做復(fù)制與粘帖示例
本篇文章主要介紹了python3+PyQt5實(shí)現(xiàn)使用剪貼板做復(fù)制與粘帖示例,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01

