透析Python 項(xiàng)目測(cè)試全過程
對(duì)于一般新入手的開發(fā)人員,如果只想在Python 項(xiàng)目測(cè)試或添加新特性之前檢查包是否能夠在他自己的平臺(tái)上進(jìn)行工作,那么 test_suite 入口點(diǎn)是非常方便的,這也避免程序員多走的彎路。
本文討論先進(jìn)的測(cè)試框架如何提供健壯的應(yīng)用程序測(cè)試自動(dòng)發(fā)現(xiàn),以及這如何替代過去維護(hù)的集中式測(cè)試列表。Python 編程社區(qū)非常重視單元測(cè)試和功能性測(cè)試。這種風(fēng)氣不但有助于確保組件和應(yīng)用程序最初的質(zhì)量,還促使程序員不斷調(diào)整和改進(jìn)代碼。
本文是討論現(xiàn)代 Python 測(cè)試框架的 三篇系列文章 的第二篇。本系列中的 ***篇文章 介紹了 zope.testing、py.test 和 nose,介紹它們?nèi)绾斡绊?Python 項(xiàng)目編寫和維護(hù)測(cè)試的方式。
本文介紹如何調(diào)用這三種框架、它們?nèi)绾卧陧?xiàng)目中發(fā)現(xiàn)測(cè)試以及如何選擇并運(yùn)行測(cè)試。***一篇文章將討論如何通過各種報(bào)告特性讓測(cè)試支持更強(qiáng)大的技術(shù)。Python 測(cè)試的黑暗時(shí)代Python 項(xiàng)目測(cè)試曾經(jīng)是非常特殊化、個(gè)人化的活動(dòng)。開發(fā)人員可能先在單獨(dú)的 Python 腳本中編寫每組測(cè)試。
然后,編寫一個(gè)名為 test_all.py 或 tests.py 的腳本,這個(gè)腳本導(dǎo)入并運(yùn)行他的所有測(cè)試。但是,無論這個(gè)過程的自動(dòng)化做得多么好,這種方式仍然是特殊化的:參與項(xiàng)目的每個(gè)開發(fā)人員都必須知道測(cè)試腳本放在哪里以及如何調(diào)用它們。
如果某個(gè) Python 開發(fā)人員從事十幾個(gè)項(xiàng)目,他就必須記住十幾個(gè)測(cè)試命令。test_all.py(或項(xiàng)目采用的其他名稱)還可能手工導(dǎo)入所有其他測(cè)試,這可能導(dǎo)致風(fēng)險(xiǎn)。如果這個(gè)集中的測(cè)試列表過時(shí)了(常常是由于開發(fā)人員添加了新的測(cè)試套件。
手工運(yùn)行它,但是忘了把它添加到中心腳本中),那么在 Python 包投入生產(chǎn)之前的***一次測(cè)試就會(huì)遺漏許多測(cè)試。這種無政府狀態(tài)的另一個(gè)缺點(diǎn)是,它要求每個(gè)測(cè)試文件包含樣板代碼,從而能夠作為單獨(dú)的命令運(yùn)行。如果查看 Python 項(xiàng)目測(cè)試或當(dāng)今的一些 Python 項(xiàng)目,會(huì)看到許多這樣的測(cè)試示例:
- # test_old.py - The old way of doing things
- import unittest
- class TruthTest(unittest.TestCase):
- def testTrue(self):
- assert True == 1
- def testFalse(self):
- assert False == 0
- if __name__ == '__main__':
- unittest.main()
本系列的 ***篇文章 已經(jīng)討論過基于 TestCase 類的測(cè)試在現(xiàn)代環(huán)境中為什么常常是不必要的。但是,現(xiàn)在注意***兩行:它們起什么作用?答案是,它們檢測(cè)什么時(shí)候從命令行單獨(dú)運(yùn)行這個(gè) test_old.py 腳本。
在這種情況下,它們運(yùn)行一個(gè) unittest 簡(jiǎn)便函數(shù),這個(gè)函數(shù)在模塊中搜索測(cè)試并運(yùn)行它們。它們使這個(gè)測(cè)試文件可以獨(dú)立于項(xiàng)目范圍的測(cè)試腳本單獨(dú)運(yùn)行。顯然,在數(shù)十甚至數(shù)百個(gè)測(cè)試模塊中復(fù)制相同的代碼非常麻煩。
另一個(gè)不太明顯的缺點(diǎn)是這種做法不利于標(biāo)準(zhǔn)化。如果 test_main() 函數(shù)不夠完善,無法檢測(cè)出某個(gè)模塊的測(cè)試,那么這個(gè)模塊的行為可能與其他測(cè)試套件不匹配。因此,每個(gè)模塊在測(cè)試類的名稱、操作方式和運(yùn)行方式方面稍有差異。
由于主流 Python 測(cè)試框架的出現(xiàn),上述的所有問題已經(jīng)解決了,而且每種框架解決這些問題的方式大致相同。首先,這三種測(cè)試框架都提供了從操作系統(tǒng)命令行運(yùn)行測(cè)試的標(biāo)準(zhǔn)方法。這樣,每個(gè) Python 項(xiàng)目測(cè)試就不再需要在代碼基中維護(hù)全局測(cè)試腳本。
zope.testing 包運(yùn)行測(cè)試的機(jī)制是最特殊化的:因?yàn)?Zope 開發(fā)人員常常使用 buildout 設(shè)置他們的項(xiàng)目,常常通過 buildout.cfg 文件中的 zc.recipe.testrunner recipe 安裝測(cè)試腳本。但是,結(jié)果在不同的項(xiàng)目上相當(dāng)一致:在我遇到的每個(gè) Zope 項(xiàng)目中,開發(fā) buildout 都會(huì)創(chuàng)建一個(gè) ./bin/test 腳本,可以通過它調(diào)用項(xiàng)目的測(cè)試。
py.test 和 nose 項(xiàng)目的做法更意思。它們都提供一個(gè)命令行工具,所以每個(gè)項(xiàng)目完全不需要有自己的測(cè)試命令:
- # Run "py.test" on the project
- # in the current directory...
- $ py.test
- # Run "nose" on the project
- # in the current directory...
- $ nosetests
py.test 和 nosetests 工具甚至有幾個(gè)相同的命令行選項(xiàng),比如 -v 選項(xiàng)在執(zhí)行測(cè)試時(shí)輸出測(cè)試的名稱。可能過不了多久,只要程序員熟悉這兩種工具,就能夠運(yùn)行大多數(shù)公共 Python 包的測(cè)試
【編輯推薦】