欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python中的測試模塊unittest和doctest的使用教程

 更新時(shí)間:2015年04月14日 10:40:18   作者:IBM  
這篇文章主要介紹了Python中的測試模塊unittest和doctest的使用教程,本文來自于IBM官方網(wǎng)站技術(shù)文檔,需要的朋友可以參考下

我要坦白一點(diǎn)。盡管我是一個(gè)應(yīng)用相當(dāng)廣泛的公共域 Python 庫的創(chuàng)造者,但在我的模塊中引入的單元測試是非常不系統(tǒng)的。實(shí)際上,那些測試大部分 是包括在 gnosis.xml.pickle 的 Gnosis Utilities 中的,并由該子軟件包(subpackage)的貢獻(xiàn)者所編寫。我還發(fā)現(xiàn),我下載的絕大多數(shù)第三方 Python 包都缺少完備的單元測試集。

不僅如此,Gnosis Utilities 中現(xiàn)有的測試也受困于另一個(gè)缺陷:您經(jīng)常需要在極其大量的細(xì)節(jié)中去推定期望的輸出,以確定測試的成敗。測試實(shí)際上 -- 在很多情況下 -- 更像是使用庫的某些部分的小實(shí)用工具。這些測試(或?qū)嵱霉ぞ撸┲С謥碜匀我鈹?shù)據(jù)源(類型正確)的輸入和/或描述性數(shù)據(jù)格式的輸出。實(shí)際上,當(dāng)您需要調(diào)試一些細(xì)微的錯(cuò)誤時(shí),這些測試實(shí)用工具更有用。但是對于庫版本間變化的自解釋的完整性檢查(sanity checks)來說,這些類測試就不能勝任了。

在這一期文章中,我嘗試使用 Python 標(biāo)準(zhǔn)庫模塊 doctest 和 unittest 來改進(jìn)我的實(shí)用工具集中的測試,并帶領(lǐng)您與我一起體驗(yàn)(并指出一些最好的方法)。

腳本 gnosis/xml/objectify/test/test_basic.py 給出了一個(gè)關(guān)于當(dāng)前測試的缺點(diǎn)及解決方案的典型示例。下面是該腳本的最新版本:

清單 1. test_basic.py

"Read and print and objectified XML file"
import sys
from gnosis.xml.objectify import XML_Objectify, pyobj_printer
if len(sys.argv) > 1:
 for filename in sys.argv[1:]:
  for parser in ('DOM','EXPAT'):
   try:
    xml_obj = XML_Objectify(filename, parser=parser)
    py_obj = xml_obj.make_instance()
    print pyobj_printer(py_obj).encode('UTF-8')
    sys.stderr.write("++ SUCCESS (using "+parser+")\n")
    print "="*50
   except:
    sys.stderr.write("++ FAILED (using "+parser+")\n")
    print "="*50
else:
 print "Please specify one or more XML files to Objectify."

實(shí)用工具函數(shù) pyobj_printer() 生成了任意 Python 對象(具體說是這樣一個(gè)對象,它既沒有用到 gnosis.xml.objectify 的任何其他實(shí)用工具,也沒有用到 Gnosis Utilities 中的 任何其他東西)的一個(gè) 非-XML 表示。在以后的版本中,我將可能會(huì)把這個(gè)函數(shù)移到 Gnosis 包內(nèi)的其他地方。無論如何, pyobj_printer() 使用各種類-Python 的縮進(jìn)和符號(hào)來描述對象和它們的屬性(類似于 pprint ,但是擴(kuò)展了實(shí)例,而不僅限于擴(kuò)展內(nèi)置的數(shù)據(jù)類型)。

如果一些特別的 XML 可能不能正確被地“對象化(objectified)”, test_basic.py 腳本會(huì)提供一個(gè)很好的調(diào)試工具 -- 您可以可視化地查看結(jié)果對象的屬性和值。此外,如果您重定向了 STDOUT,您可以查看 STDERR 上的簡單消息,如這個(gè)例子中:

清單 2. 分析 STDERR 結(jié)果消息

$ python test_basic.py testns.xml > /dev/null
++ SUCCESS (using DOM)
++ FAILED (using EXPAT)

不過,上面運(yùn)行的例子中對成功或失敗的界定很不明顯:成功只是意味著沒有出現(xiàn)異常,而不表示(重定向的)輸出 正確。
使用 doctest


doctest 模塊讓您可以在文檔字符串(docstrings)內(nèi)嵌入注釋以顯示各種語句的期望行為,尤其是函數(shù)和方法的結(jié)果。這樣做很像是讓文檔字符串看起來如同一個(gè)交互式 shell 會(huì)話;完成這一任務(wù)的一個(gè)簡單方法是,從一個(gè) Python 交互式 shell 中(或者從 Idel、PythonWin、MacPython 或者其他帶有交互式會(huì)話的 IDE 中)拷貝-粘貼。這一改進(jìn)的 test_basic.py 腳本舉例說明了自診斷功能的添加:

清單 3. 具有自診斷功能的 test_basic.py 腳本

import sys
from gnosis.xml.objectify import XML_Objectify, pyobj_printer, EXPAT, DOM
LF = "\n"
def show(xml_src, parser):
 """Self test using simple or user-specified XML data
 >>> xml = '''<?xml version="1.0"?>
 ... <!DOCTYPE Spam SYSTEM "spam.dtd" >
 ... <Spam>
 ... <Eggs>Some text about eggs.</Eggs>
 ... <MoreSpam>Ode to Spam</MoreSpam>
 ... </Spam>'''
 >>> squeeze = lambda s: s.replace(LF*2,LF).strip()
 >>> print squeeze(show(xml,DOM)[0])
 -----* _XO_Spam *-----
 {Eggs}
  PCDATA=Some text about eggs.
 {MoreSpam}
  PCDATA=Ode to Spam
 >>> print squeeze(show(xml,EXPAT)[0])
 -----* _XO_Spam *-----
 {Eggs}
  PCDATA=Some text about eggs.
 {MoreSpam}
  PCDATA=Ode to Spam
 PCDATA=
 """
 try:
  xml_obj = XML_Objectify(xml_src, parser=parser)
  py_obj = xml_obj.make_instance()
  return (pyobj_printer(py_obj).encode('UTF-8'),
    "++ SUCCESS (using "+parser+")\n")
 except:
  return ("","++ FAILED (using "+parser+")\n")
if __name__ == "__main__":
 if len(sys.argv)==1 or sys.argv[1]=="-v":
  import doctest, test_basic
  doctest.testmod(test_basic)
 elif sys.argv[1] in ('-h','-help','--help'):
  print "You may specify XML files to objectify instead of self-test"
  print "(Use '-v' for verbose output, otherwise no message means success)"
 else:
  for filename in sys.argv[1:]:
   for parser in (DOM, EXPAT):
    output, message = show(filename, parser)
    print output
    sys.stderr.write(message)
    print "="*50

注意,我在經(jīng)過改進(jìn)(和擴(kuò)展)的測試腳本中放入了 main 代碼塊,這樣,如果您在命令行中指定了 XML 文件,腳本將繼續(xù)執(zhí)行以前的行為。這樣就讓您可以繼續(xù)分析測試用例以外其他的 XML,并只著眼于結(jié)果 -- 或者找出 gnosis.xml.objectify 所做事情中的錯(cuò)誤,或者只是理解其目的。按標(biāo)準(zhǔn)的方式,您可以使用 -h 或 --help 參數(shù)來獲得用法的說明。

當(dāng)不帶任何參數(shù)(或者帶有只被 doctest 使用的 -v 參數(shù))運(yùn)行 test_basic.py 時(shí),就會(huì)發(fā)現(xiàn)有趣的新功能。在這個(gè)例子中,我們在模塊/腳本自身上運(yùn)行 doctest -- 您可以看到,實(shí)際上我們將 test_basic 導(dǎo)入到腳本自己的名稱空間中,這樣我們可以簡單地導(dǎo)入其他希望要測試的模塊。 doctest.testmod() 函數(shù)去遍歷模塊本身、它的函數(shù)以及它的類中的所有文檔字符串,以找出所有類似交互式 shell 會(huì)話的內(nèi)容;在這個(gè)例子中,會(huì)在 show() 函數(shù)中找到這樣一個(gè)會(huì)話。

show() 的文檔字符串舉例說明了在設(shè)計(jì)好的 doctest 會(huì)話過程中的幾個(gè)小“陷阱(gotchas)”。不幸的是, doctest 在解析顯式會(huì)話時(shí),將空行作為會(huì)話結(jié)束來處理 -- 所以,像 pyobj_printer() 的返回值這樣的輸出需要加一些保護(hù)(be munged slightly)以進(jìn)行測試。最簡單的途徑是使用文檔字符串本身所定義的像 squeeze() 這樣的函數(shù)(它只是除去緊跟在后面的換行)。此外,由于文檔字符串畢竟是字符串換碼(escape),所以 \n 這樣的序列被擴(kuò)展,這樣使得在代碼示例 內(nèi)部對換行進(jìn)行換碼稍微有一些混亂。您可以使用 \\n ,不過我發(fā)現(xiàn)對 LF 的定義解決了這些問題。

在 show() 的文檔字符串中定義的自測試所做的不僅是確保不發(fā)生異常(對照于最初的測試腳本)。為正確的“對象化(objectification)”至少要檢查一個(gè)簡單的 XML 文檔。當(dāng)然,仍然有可能不能正確地處理一些其他的 XML 文檔 -- 例如,上面我們試過的名稱空間 XML 文檔 testns.xml 遇到了 EXPAT 解析器失敗。由 doctest處理的文檔字符串 可能會(huì)在其內(nèi)部包含回溯(traceback),但是在特別的情況下,更好的方法是使用 unittest 。

使用 unittest


另一個(gè)包含在 gnosis.xml.objectify 中的測試是 test_expat.py 。創(chuàng)建這一測試的主要原因僅在于,使用 EXPAT 解析器的子軟件包用戶常常需要調(diào)用一個(gè)特別的設(shè)置函數(shù)來啟用有名稱空間的 XML 文檔的處理(這個(gè)實(shí)際情況是演化來的而不是設(shè)計(jì)如此,并且以后可能會(huì)改變)。老的測試會(huì)試圖不借助設(shè)置去打印對象,如果發(fā)生異常則捕獲之,然后如果需要的話借助設(shè)置再去打?。ú⒔o出一個(gè)關(guān)于所發(fā)生事情的消息)。

而如果使用 test_basic.py , test_expat.py 工具讓您可以分析 gnosis.xml.objectify 如何去描述一個(gè)新奇的 XML 文檔。但是與以前一樣,有很多我們可能想去驗(yàn)證的具體行為。 test_expat.py 的一個(gè)增強(qiáng)的、擴(kuò)展的版本使用 unittest 來分析各種動(dòng)作執(zhí)行時(shí)發(fā)生的事情,包括持有特定條件或(近似)等式的斷言,或出現(xiàn)期望的某些異常??匆豢矗?/p>

清單 4. 自診斷的 test_expat.py 腳本

"Objectify using Expat parser, namespace setup where needed"
import unittest, sys, cStringIO
from os.path import isfile
from gnosis.xml.objectify import make_instance, config_nspace_sep,\
         XML_Objectify
BASIC, NS = 'test.xml','testns.xml'
class Prerequisite(unittest.TestCase):
 def testHaveLibrary(self):
  "Import the gnosis.xml.objectify library"
  import gnosis.xml.objectify
 def testHaveFiles(self):
  "Check for sample XML files, NS and BASIC"
  self.failUnless(isfile(BASIC))
  self.failUnless(isfile(NS))
class ExpatTest(unittest.TestCase):
 def setUp(self):
  self.orig_nspace = XML_Objectify.expat_kwargs.get('nspace_sep','')
 def testNoNamespace(self):
  "Objectify namespace-free XML document"
  o = make_instance(BASIC)
 def testNamespaceFailure(self):
  "Raise SyntaxError on non-setup namespace XML"
  self.assertRaises(SyntaxError, make_instance, NS)
 def testNamespaceSuccess(self):
  "Sucessfully objectify NS after setup"
  config_nspace_sep(None)
  o = make_instance(NS)
 def testNspaceBasic(self):
  "Successfully objectify BASIC despite extra setup"
  config_nspace_sep(None)
  o = make_instance(BASIC)
 def tearDown(self):
  XML_Objectify.expat_kwargs['nspace_sep'] = self.orig_nspace
if __name__ == '__main__':
 if len(sys.argv) == 1:
  unittest.main()
 elif sys.argv[1] in ('-q','--quiet'):
  suite = unittest.TestSuite()
  suite.addTest(unittest.makeSuite(Prerequisite))
  suite.addTest(unittest.makeSuite(ExpatTest))
  out = cStringIO.StringIO()
  results = unittest.TextTestRunner(stream=out).run(suite)
  if not results.wasSuccessful():
   for failure in results.failures:
    print "FAIL:", failure[0]
   for error in results.errors:
    print "ERROR:", error[0]
 elif sys.argv[1].startswith('-'): # pass args to unittest
  unittest.main()
 else:
  from gnosis.xml.objectify import pyobj_printer as show
  config_nspace_sep(None)
  for fname in sys.argv[1:]:
   print show(make_instance(fname)).encode('UTF-8')

使用 unittest 為較簡單的 doctest 方式增添了相當(dāng)多的能力。我們可以將我們的測試分為幾個(gè)類,每一個(gè)類都繼承自 unittest.TestCase 。在每一個(gè)測試類內(nèi)部,每一個(gè)名稱以“.test”開始的方法都被認(rèn)為是另一個(gè)測試。為 ExpatTest 定義的兩個(gè)額外的類很有趣:在每次使用類執(zhí)行測試前運(yùn)行 .setUp() ,測試結(jié)束時(shí)運(yùn)行 .tearDown() (不管測試是成功、失敗還是出現(xiàn)錯(cuò)誤)。在我們上面的例子中,我們?yōu)閷S玫?expat_kwargs 字典做了一點(diǎn)簿記以確保每個(gè)測試獨(dú)立地運(yùn)行。

順便提一下,失敗(failure)和錯(cuò)誤(error)之間的區(qū)別很重要。一個(gè)測試可能會(huì)因?yàn)橐恍┚唧w的斷言無效而失?。〝嘌苑椒ɑ蛘咭浴?fail”開頭,或者以“.assert”開頭)。在某種意義上,失敗是期望中的 -- 最起碼從某種意義上我們已經(jīng)具體分析過。另一方面,錯(cuò)誤是意外的問題 -- 因?yàn)槲覀兪孪炔恢滥睦飼?huì)出錯(cuò),我們需要分析實(shí)際測試運(yùn)行中的回溯來診斷這種問題。不過,我們可以設(shè)計(jì)讓失敗給出診斷錯(cuò)誤的提示。例如,如果 Prerequisite.haveFiles() 失敗,將在一些 TestExpat 測試中出現(xiàn)錯(cuò)誤;如果前者是成功的,您將不得不到其他地方去查找錯(cuò)誤的根源。

在 unittest.TestCase 的繼承類中,具體的測試方法中可能會(huì)包括一些 .assert...() 或者 .fail...() 方法,但也可能只是具有一系列我們相信應(yīng)該會(huì)成功執(zhí)行的動(dòng)作。如果測試方法沒有按預(yù)期運(yùn)行,我們將得到一個(gè)錯(cuò)誤(以及描述這個(gè)錯(cuò)誤的回溯)。

test_expat.py 中的 _main_ 程序塊也值得察看。在最簡單的情況下,我們可以只使用 unittest.main() 來運(yùn)行測試用例,這將斷定哪些需要運(yùn)行。使用這種方式時(shí), unittest 模塊將接受一個(gè) -v 選項(xiàng)以給出更詳細(xì)的輸出。根據(jù)指定的文件名,在執(zhí)行了名稱空間設(shè)置后,我們打印出指定的 XML 文件的表示,從而大致上保持了對此工具稍老版本的向后兼容。

_main_ 中最有趣的分支是期待 -q 或 --quiet 標(biāo)簽的那個(gè)分支。如您將期望的,除非發(fā)生失敗或錯(cuò)誤,否則這個(gè)分支將是靜默的(quiet,即盡量減少輸出)。不僅如此,由于它是靜默的,它只會(huì)為每個(gè)問題顯示一行關(guān)于失敗/錯(cuò)誤位置的報(bào)告,而不是整個(gè)診斷回溯。除了對靜默輸出風(fēng)格的直接利用以外,這個(gè)分支還舉例說明了相對于測試套件的自定義測試以及對結(jié)果報(bào)告的控制。稍微有些長的 unittest.TextTestRunner() 的默認(rèn)輸出被定向到 StringIO out -- 如果您想查看它,歡迎您到 out.getvalue() 去查找。不過, result 對象讓我們對全面成功進(jìn)行測試,如果不是完全成功還可以讓我們處理失敗和錯(cuò)誤。顯然,由于它們是變量中的值,您可以輕松地將 result 對象的內(nèi)容記錄入日志,或者在 GUI 中顯示,不管怎么樣,不是僅僅打印到 STDOUT。

組合測試


可能 unittest 框架最好的特性是讓您可以輕松地組合包含不同模塊的測試。實(shí)際上,如果使用 Python 2.3+,您甚至可以將 doctest 測試轉(zhuǎn)化為 unittest 套件。讓我們將到目前為止所創(chuàng)建的測試組合到一個(gè)腳本 test_all.py 中(誠然,說它是我們目前為止所做的測試有些夸張):

清單 5. test_all.py 組合了單元測試

"Combine tests for gnosis.xml.objectify package (req 2.3+)"
import unittest, doctest, test_basic, test_expat
suite = doctest.DocTestSuite(test_basic)
suite.addTest(unittest.makeSuite(test_expat.Prerequisite))
suite.addTest(unittest.makeSuite(test_expat.ExpatTest))
 unittest.TextTestRunner(verbosity=2).run(suite)

由于 test_expat.py 只是包含測試類,所以它們可以容易地添加到本地的測試套件中。 doctest.DocTestSuite() 函數(shù)執(zhí)行文檔字符串測試的轉(zhuǎn)換。讓我們來看看 test_all.py 運(yùn)行時(shí)會(huì)做什么:

清單 6. 來自 test_all.py 的成功輸出

$ python2.3 test_all.py
doctest of test_basic.show ... ok
Check for sample XML files, NS and BASIC ... ok
Import the gnosis.xml.objectify library ... ok
Raise SyntaxError on non-setup namespace XML ... ok
Sucessfully objectify NS after setup ... ok
Objectify namespace-free XML document ... ok
Successfully objectify BASIC despite extra setup ... ok
----------------------------------------------------------------------
Ran 7 tests in 0.052s
OK

注意對執(zhí)行的測試的描述:在使用 unittest 測試方法的情況下,他們的描述來自于相應(yīng)的 docstring 函數(shù)。如果您沒有指定文檔字符串,類和方法名被用作最合適的描述。來看一下如果一些測試失敗時(shí)我們會(huì)得到什么,同樣有趣(為本文去掉了回溯細(xì)節(jié)):

清單 7. 當(dāng)一些測試失敗時(shí)的結(jié)果

$ mv testns.xml testns.xml# && python2.3 test_all.py 2>&1 | head -7
doctest of test_basic.show ... ok
Check for sample XML files, NS and BASIC ... FAIL
Import the gnosis.xml.objectify library ... ok
Raise SyntaxError on non-setup namespace XML ... ERROR
Sucessfully objectify NS after setup ... ERROR
Objectify namespace-free XML document ... ok
Successfully objectify BASIC despite extra setup ... ok

隨便提及,這個(gè)失敗寫到 STDERR 的最后一行是“FAILED (failures=1, errors=2)”,如果您需要的話這是一個(gè)很好的總結(jié)(相對于成功時(shí)最終的“OK”)。

從這里開始


本文向您介紹了 unittest 和 doctest 的一些典型用法,它們已經(jīng)改進(jìn)了我自己的軟件中的測試。閱讀 Python 文檔,以深入了解可用于測試套件、測試用例和測試結(jié)果的全部范圍的方法。它們?nèi)慷甲裱又兴枋龅哪J健?/p>

讓自己遵從 Python 的標(biāo)準(zhǔn)測試模塊規(guī)定的方法學(xué)是良好的軟件實(shí)踐。測試驅(qū)動(dòng)(test-driven)的開發(fā)在很多軟件周期中都很流行;不過,顯然 Python 是一門適合于測試驅(qū)動(dòng)模型的語言。而且,如果只是考慮軟件包更可能按計(jì)劃工作,一個(gè)軟件包或庫如果伴隨有一組周全的測試,會(huì)比缺乏這些測試的軟件包或庫對用戶更為有用。

相關(guān)文章

  • python讀取xlsx的方法

    python讀取xlsx的方法

    今天小編就為大家分享一篇python讀取xlsx的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • Python實(shí)現(xiàn)的從右到左字符串替換方法示例

    Python實(shí)現(xiàn)的從右到左字符串替換方法示例

    這篇文章主要介紹了Python實(shí)現(xiàn)的從右到左字符串替換方法,涉及Python字符串遍歷、運(yùn)算、判斷、替換等相關(guān)操作技巧,需要的朋友可以參考下
    2018-07-07
  • pyinstaller打包程序exe踩過的坑

    pyinstaller打包程序exe踩過的坑

    這篇文章主要介紹了pyinstaller打包exe踩過的坑,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Python使用docx模塊編輯Word文檔

    Python使用docx模塊編輯Word文檔

    docx提供了一組功能豐富的函數(shù)和方法,用于創(chuàng)建、修改和讀取Word文檔,Python可以用它對word文檔進(jìn)行大批量的編輯,下面小編就來通過一些示例為大家好好講講吧
    2023-07-07
  • python文件處理fileinput使用方法詳解

    python文件處理fileinput使用方法詳解

    這篇文章主要介紹了python文件處理fileinput使用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • python開發(fā)之tkinter實(shí)現(xiàn)圖形隨鼠標(biāo)移動(dòng)的方法

    python開發(fā)之tkinter實(shí)現(xiàn)圖形隨鼠標(biāo)移動(dòng)的方法

    這篇文章主要介紹了python開發(fā)之tkinter實(shí)現(xiàn)圖形隨鼠標(biāo)移動(dòng)的方法,涉及Python基于tkinter繪圖的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-11-11
  • Flask框架的學(xué)習(xí)指南之制作簡單blog系統(tǒng)

    Flask框架的學(xué)習(xí)指南之制作簡單blog系統(tǒng)

    本文是Flask框架的學(xué)習(xí)指南系列文章的第二篇主要給大家講述制作一個(gè)簡單的小項(xiàng)目blog系統(tǒng)的過程,有需要的小伙伴可以參考下
    2016-11-11
  • Python實(shí)現(xiàn)的簡單模板引擎功能示例

    Python實(shí)現(xiàn)的簡單模板引擎功能示例

    這篇文章主要介紹了Python實(shí)現(xiàn)的簡單模板引擎功能,結(jié)合具體實(shí)例形式分析了Python模版引擎的定義與使用方法,需要的朋友可以參考下
    2017-09-09
  • Python使用Scrapy保存控制臺(tái)信息到文本解析

    Python使用Scrapy保存控制臺(tái)信息到文本解析

    這篇文章主要介紹了Python使用Scrapy保存控制臺(tái)信息到文本解析,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2017-12-12
  • Python時(shí)間管理黑科技之datetime函數(shù)詳解

    Python時(shí)間管理黑科技之datetime函數(shù)詳解

    在Python中,datetime模塊是處理日期和時(shí)間的標(biāo)準(zhǔn)庫,它提供了一系列功能強(qiáng)大的函數(shù)和類,用于處理日期、時(shí)間、時(shí)間間隔等,本文將深入探討datetime模塊的使用方法,感興趣的可以了解下
    2023-08-08

最新評論