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