2010-07-14 67 views
8

我在寫一個python模塊,我想單元測試它。我是python的新手,並且有些選項可用。python模塊的單元測試基礎結構

目前,我想寫我的測試爲doctests,因爲我喜歡聲明而不是命令式的風格(不過,如果它被誤解,請隨時解除這種偏好)。然而,這引出了一些問題:

  1. 我應該在哪裏進行測試?在與他們正在測試的代碼相同的文件中(或者在文檔測試中使用docstrings)?或者認爲將它們分離到自己的目錄中會更好?
  2. 如何從命令行一次性運行整個模塊中的所有測試?
  3. 如何報告測試套件的代碼覆蓋率?
  4. 任何其他的最佳實踐,我應該知道在python單元測試?

回答

12

隨意打消了我這個 偏好如果是誤傳

我相信我用doctest更廣泛(方式拉伸達到預定可使用邊界)比其他任何開源開發者,至少在一個項目中 - 全部我的gmpy項目中的測試是doctests。這是在gmpy開始的時候全新的,這似乎是一個很棒的小動作,如果有什麼值得做的話值得做得過分 - 對吧?)

錯了。除了gmpy之外,重做所有適當的單元測試都會造成過多的返工,我再也沒有犯過這個錯誤:現在,我使用單元測試作爲單元測試,並且只是爲了檢查我的文檔,就像他們一直本來打算使用。什麼樣的doctests(比較期望與實際結果的平等 - 這就是全部)僅僅是建立一個堅實的測試套件不是一個好的或合理的基礎。它從來沒有打算另有其他。我想推薦你看看nose。新Python 2.7中的unittest模塊更加豐富和更好,如果您在2.4,2.5或2.6版卡住,仍然可以使用unittest2的新功能,您可以下載並安裝這些功能; nose補充unittest相當不錯。

如果你不能進行unittest測試(但 - 試試看,它會增長!),也許試試py.test,這是一個具有完全不同理念的替代軟件包。

但是,,不要伸展doctest測試文檔中的示例以外的東西!確切的平等的比較會站在你的方式過於頻繁,因爲我已經學會在gmpy我(比喻;-)費用......

3

我不喜歡的doctests這些原因:

  • 您無法運行測試的子集。當測試失敗時,僅運行一個測試就很有用。 Doctest沒有辦法做到這一點。
  • 如果在doctest中間發生故障,整個事情就會停止。我寧願看到所有的結果來決定如何處理破損。
  • 編碼風格是風格化的,必須具有可打印的結果。
  • 您的代碼以特殊方式執行,因此很難推斷它將如何執行,難以添加幫助程序,並且難以編程測試。

這個清單取自我的博客文章Things I don't like about doctest,那裏有更多的內容,還有很長一段話評論這些觀點。

關於範圍:我不相信有一個Python覆蓋工具可以測量doctests內的覆蓋率。但是,由於它們僅僅是沒有分支或循環的陳述清單,這是一個問題嗎?

+0

對不起,我不是故意的文檔測試的*覆蓋*,通過運行文檔測試行使代碼的更多報道。但是,在你的建議和亞歷克斯之間,我深信不要使用它們,而是去單元測試! – fmark 2010-07-14 02:27:48

+0

當然,接受@ bstpiere有關coverage.py的建議! :) – 2010-07-14 02:30:01

1

如需報道,請查看出色的coverage.py

否則,Alex Martelli寫的所有內容都非常重要。

1

doctests非常適合於快速的次要單元測試,它描述了所討論對象的一些基本用法(如它們出現在docstrings中,因此有助於(不管))等。

我個人發現使用unittest模塊可以更有效地進行更廣泛和更徹底的測試,現在2.7模塊(返回portlet到unittest2)擁有更多便利的斷言。您可以使用單元測試框架設置測試套件和複雜的場景,並且一次覆蓋整個不同測試的各個區域(命令行方式)。

coverage.py,由Ned Batchelder和@bstpierre提到將與這兩者中的任何一個一起工作,並且我推薦它看看你已經測試了哪些代碼,哪些沒有。您可以將其添加到CI系統(即Hudson或任何您喜歡使用的系統)以跟上所覆蓋的內容,而HTML報告非常適合查看未通過測試覆蓋的內容。 Coverage支持Junit xml輸出,許多CI系統知道如何提供正在繪製的結果,讓您看到構建隨着時間的推移變得更好或更糟。

2

我有這種懷疑,Alex在程序員的曲線上可能比我早一點,但是如果你想要某些具有某些Python體驗的人的角度(作爲「用戶」而不是專家或傳道者),還不在同一聯盟中,我關於單元測試的發現幾乎一樣。

Doctests在開始階段對於簡單的測試聽起來很不錯,而且我在這個方向上進行了一些在家中的個人項目,因爲這是在其他地方推薦的。 在工作中,我們使用鼻子(儘管如此罐裝和包裝,我的印象是,我們一直使用pyUnit,直到不久之前),幾個月前,我也搬到家裏。

最初的設置時間和管理開銷以及與實際代碼的分離在開始時可能看起來沒有必要,特別是當您測試的代碼量不是很大時,但從長遠來看,我發現文檔測試阻礙了我想要做的每一次重構或重組,而且很難維護,幾乎不可能擴展,並且很快就抵消了最初的儲蓄。是的,我知道單元測試與集成測試不太一樣,但是doctests傾向於爲你定義你的單元,而不是太嚴格。 如果您認爲它是一個有效的草圖工具或開發模型,它們也不太適合基於單元的敏捷。

這可能需要你計劃一下,然後再用pyUnit或nose引導你進行單元測試,但是很可能在短期內你會發現它實際上在很多層面上幫助你。我知道它對我有用,對於我目前工作的代碼庫的複雜性和規模而言,我相對較新。剛開始的幾周就必須咬緊牙關。

0

我同意上面提到的有關doctest不縮放的所有上述問題,我更願意堅持單元測試。

我可以貢獻的一點是從代碼處理__name__ == "__main__調用單元測試,因此如果測試文件作爲腳本運行,它將運行測試。

如:

#!/usr/bin/env python 

""" 
Unit tests for the GetFiles.py utility 
""" 

import unittest 
from FileUtilities import getTree 

class TestFileUtilities(unittest.TestCase): 

    def testGetTree(self): 
     """ 
     Tests that a known tree is found and incidentally confirms 
     that we have the tree we expected to use for our current 
     sample extraction. 
     """ 
     found = getTree('./anzmeta-dtd', '.pen') 
     expected_path_tail = ['ISOdia.pen', 
         'ISOgrk1.pen', 
         'ISOtech.pen'] 
     for i, full_path in enumerate(found): 
     assert full_path.endswith(expected_path_tail[i]), expected_path_tail[i] 

# other tests elided   

if __name__ == "__main__": 
    # When this module is executed from the command-line, run all its tests 
    unittest.main()