1. 程式人生 > >python測試框架及應用

python測試框架及應用

大型軟體系統的開發是一個很複雜的過程,其中因為人的因素而所產生的錯誤非常多,因此軟體在開發過程必須要有相應的質量保證活動,而軟體測試則是保證質量的關鍵措施。正像軟體熵(software entropy)所描述的那樣:一個程式從設計很好的狀態開始,隨著新的功能不斷地加入,程式逐漸地失去了原有的結構,最終變成了一團亂麻(其實最初的"很好的狀態"得加個問號)。測試的目的說起來其實很簡單也極具吸引力,那就是寫出高質量的軟體並解決軟體熵這一問題。

可惜的是,軟體開發人員很少能在編碼的過程中就進行軟體測試,大部分軟體專案都只在最終驗收時才進行測試,有些專案甚至根本沒有測試計劃!隨著軟體質量意識的增強,許多軟體開發組織開始轉向UML、CMM、RUP、XP等軟體工程方法,以期提高軟體質量,並使軟體開發過程更加可控,好在這些方法對測試都提出了很嚴格的要求,從而使得測試在軟體開發過程的作用開始真正體現出來。

軟體測試作為一種系統工程,涉及到整個軟體開發過程的各個方面,需要管理人員、設計人員、開發人員和測試人員的共同努力。作為軟體開發過程中的主要力量,現今的程式設計師除了要編寫實現程式碼外,還承擔著單元測試這一艱鉅任務,因此必須採用新的工作模式:

  • 編寫和維護一套詳盡的單元測試用例;
  • 先構造單元測試和驗收測試用例,然後再編寫程式碼;
  • 根據構造的測試用例來編寫程式碼。

單元測試負責對最小的軟體設計單元(模組)進行驗證,它使用軟體設計文件中對模組的描述作為指南,對重要的程式分支進行測試以發現模組中的錯誤。由於軟體模組並不是一個單獨的程式,為了進行單元測試還必須編寫大量額外的程式碼,從而無形中增加了開發人員的工作量,目前解決這一問題比較好的方法是使用測試框架。測試框架是在用XP方法進行單元測試時的關鍵,尤其是在需要構造大量測試用例時更是如此,因為如果完全依靠手工的方式來構造和執行這些測試,肯定會變成一個花費大量時間並且單調無味的工作,而測試框架則可以很好地解決這些問題。

使用Python語言的開發人員可以使用Steve Purcell編寫的PyUnit作為單元測試框架,通過將單元測試融合到PyUnit這一測試框架裡,Python程式設計師可以更容易地增加、管理、執行測試用例,並對測試結果進行分析。此外,使用PyUnit還可以實現自動單元測試(迴歸測試)。

回頁首

測試是一個貫穿於整個開發過程的連續過程,從某個意義上說,軟體開發的過程實際上就是測試過程。正如Martin Fowler所說的"在你不知道如何測試程式碼之前,就不該編寫程式。而一旦你完成了程式,測試程式碼也應該完成。除非測試成功,你不能認為你編寫出了可以工作的程式。"

測試最基本的原理就是比較預期結果是否與實際執行結果相同,如果相同則測試成功,否則測試失敗。為了更好地理解PyUnit這一自動測試框架的作用,先來看一個簡單的例子,假設我們要對例1中的Widget類進行測試:

        例1. widget.py
# 將要被測試的類
class Widget:
    def __init__(self, size = (40, 40)):
        self._size = size
    def getSize(self):
        return self._size
    def resize(self, width, height):
        if width  0  or height < 0:
            raise ValueError, "illegal size"
        self._size = (width, height)
    def dispose(self):
        pass
        

採用手工方式進行單元測試的Python程式設計師很可能會寫出類似例2的測試程式碼來,

    例2. manual.py
from widget import Widget
# 執行測試的類
class TestWidget:
    def testSize(self):
        expectedSize = (40, 40);
        widget = Widget()
        if widget.getSize() == expectedSize:
            print "test [Widget]: getSize works perfected!"
        else:
            print "test [Widget]: getSize doesn't work!"
# 測試
if __name__ == '__main__':
    myTest = TestWidget()
    myTest.testSize() 
    

稍一留心你不難發現這種手工測試方法存在許多問題。首先,測試程式的寫法沒有一定的規範可以遵循,十個程式設計師完全可能寫出十種不同的測試程式來,如果每個Python程式設計師都有自己不同的設計測試類的方法,光維護被測試的類就夠麻煩了,誰還顧得上維護測試類。其次,需要編寫大量的輔助程式碼才能進行單元測試,例1中用於測試的程式碼甚至比被測試的程式碼還要多,而這毫無疑問將增大Python程式設計師的工作量。

為了讓單元測試程式碼能夠被測試和維護人員更容易地理解,最好的解決辦法是讓開發人員遵循一定的規範來編寫用於測試的程式碼,具體到Python程式設計師來講,則是要採用PyUnit這一自動測試框架來構造單元測試用例。目前PyUnit已經得到了大多數Python開發人員的認可,成了事實上的單元測試標準。如果採用PyUnit來進行同樣的測試,則測試程式碼將如例3所示:

    例3. auto.py
from widget import Widget
import unittest
# 執行測試的類
class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget()
    def tearDown(self):
        self.widget = None
    def testSize(self):
        self.assertEqual(self.widget.getSize(), (40, 40))
# 構造測試集
def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase("testSize"))
    return suite
# 測試
if __name__ == "__main__":
    unittest.main(defaultTest = 'suite')
    

在採用PyUnit這一單元測試框架後,用於測試的程式碼做了相應的改動:

  • 用import語句引入unittest模組。
  • 讓所有執行測試的類都繼承於TestCase類,可以將TestCase看成是對特定類進行測試的方法的集合。
  • 在setUp()方法中進行測試前的初始化工作,並在tearDown()方法中執行測試後的清除工作,setUp()和tearDown()都是TestCase類中定義的方法。
  • 在testSize()中呼叫assertEqual()方法,對Widget類中getSize()方法的返回值和預期值進行比較,確保兩者是相等的,assertEqual()也是TestCase類中定義的方法。
  • 提供名為suite()的全域性方法,PyUnit在執行測試的過程呼叫suit()方法來確定有多少個測試用例需要被執行,可以將TestSuite看成是包含所有測試用例的一個容器。

雖然看起來有點複雜,但PyUnit使得所有的Python程式設計師都可以使用同樣的單元測試方法,測試過程不再是雜亂無章的了,而是在同一規範指導下進行的有序行為,這就是使用PyUnit這一自動單元測試框架所帶來的最大好處。

回頁首

在對軟體測試理論和PyUnit有了一個大致瞭解之後,下面輔以具體的例項介紹Python程式設計師如何藉助PyUnit來進行單元測試。所有的程式碼均在Python 2.2.2下除錯通過,作業系統使用的是Red Hat Linux 9。

3.1 安裝

在Python中進行單元測試時需要用到PyUnit模組,Python 2.1及其以後的版本都將PyUnit作為一個標準模組,但如果你使用的是較老版本的Python,那就要自已動手安裝了。在PyUnit的網站(http://sourceforge.net/projects/pyunit)上可以下載到PyUnit最新的原始碼包,此處使用的是pyunit-1.4.1.tar.gz。

在下載好PyUnit軟體包後,執行下面的命令對其進行解壓縮:

[[email protected] source]# tar xzvf pyunit-1.4.1.tar.gz

要在Python程式中使用PyUnit模組,最簡單的辦法是確保PyUni軟體包中的檔案unittest.py和unittestgui.py都包含在Python的搜尋路徑中,這既可以通過直接設定PYTHONPATH環境變數來實現,也可以執行以下的命令來將它們複製到Python的當前搜尋路徑中:

[[email protected] source]# cd pyunit-1.4.1
[[email protected] pyunit-1.4.1]# python setup.py install

回頁首

軟體測試中最基本的組成單元是測試用例(test case),PyUnit使用TestCase類來表示測試用例,並要求所有用於執行測試的類都必須從該類繼承。TestCase子類實現的測試程式碼應該是自包含(self contained)的,也就是說測試用例既可以單獨執行,也可以和其它測試用例構成集合共同執行。

TestCase在PyUnit測試框架中被視為測試單元的執行實體,Python程式設計師可以通過它派生自定義的測試過程與方法(測試單元),利用Command和Composite設計模式,多個TestCase還可以組合成測試用例集合。PyUnit測試框架在執行一個測試用例時,TestCase子類定義的setUp()、runTest()和tearDown()方法被依次執行,最簡單的測試用例只需覆蓋runTest()方法來執行特定的測試程式碼就可以了,如例4所示:

        例4. static_single.py
import unittest
# 執行測試的類
class WidgetTestCase(unittest.TestCase):
    def runTest(self):
        widget = Widget()
        self.assertEqual(widget.getSize(), (40, 40))
        

而要在PyUnit測試框架中構造上述WidgetTestCase類的一個例項,應該不帶任何引數呼叫其建構函式:

testCase = WidgetTestCase()

一個測試用例通常只對軟體模組中的一個方法進行測試,採用覆蓋runTest()方法來構造測試用例在PyUnit中稱為靜態方法,如果要對同一個軟體模組中的多個方法進行測試,通常需要構造多個執行測試的類,如例5所示:

        例5. static_multi.py
import unittest
# 測試getSize()方法的測試用例
class WidgetSizeTestCase(unittest.TestCase):
    def runTest(self):
        widget = Widget()
        self.assertEqual(widget.getSize(), (40, 40))
# 測試resize()方法的測試用例
class WidgetResizeTestCase(unittest.TestCase):
    def runTest(self):
        widget = Widget()
        widget.resize(100, 100)
        self.assertEqual(widget.getSize(), (100, 100))
        

採用靜態方法,Python程式設計師不得不為每個要測試的方法編寫一個測試類(該類通過覆蓋runTest()方法來執行測試),並在每一個測試類中生成一個待測試的物件。在為同一個軟體模組編寫測試用例時,很多時候待測物件有著相同的初始狀態,因此採用上述方法的Python程式設計師不得不在每個測試類中為待測物件進行同樣的初始化工作,而這往往是一項費時且枯燥的工作。

一種更好的解決辦法是採用PyUnit提供的動態方法,只編寫一個測試類來完成對整個軟體模組的測試,這樣物件的初始化工作可以在setUp()方法中完成,而資源的釋放則可以在tearDown()方法中完成,如例6所示:

        例6. dynamic.py
import unittest
# 執行測試的類
class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget()
    def tearDown(self):
        self.widget.dispose()
        self.widget = None
    def testSize(self):
        self.assertEqual(self.widget.getSize(), (40, 40))
    def testResize(self):
        self.widget.resize(100, 100)
        self.assertEqual(self.widget.getSize(), (100, 100))
        

採用動態方法最大的好處是測試類的結構非常好,用於測試一個軟體模組的所有程式碼都可以在同一個類中實現。動態方法不再覆蓋runTest()方法,而是為測試類編寫多個測試方法(按習慣這些方法通常以test開頭),在建立TestCase子類的例項時必須給出測試方法的名稱,來為PyUnit測試框架指明執行該測試用例時究竟應該呼叫測試類中的哪個方法:

sizeTestCase = WidgetTestCase("testSize")
resizeTestCase = WidgetTestCase("testResize")

完整的單元測試很少只執行一個測試用例,開發人員通常都需要編寫多個測試用例才能對某一軟體功能進行比較完整的測試,這些相關的測試用例稱為一個測試用例集,在PyUnit中是用TestSuite類來表示的。

在建立了一些TestCase子類的例項作為測試用例之後,下一步要做的工作就是用TestSuit類來組織它們。PyUnit測試框架允許Python程式設計師在單元測試程式碼中定義一個名為suite()的全域性函式,並將其作為整個單元測試的入口,PyUnit通過呼叫它來完成整個測試過程。

def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase("testSize"))
    suite.addTest(WidgetTestCase("testResize"))
    return suite

也可以直接定義一個TestSuite的子類,並在其初始化方法(__init__)中完成所有測試用例的新增:

                                               class WidgetTestSuite(unittest.TestSuite):
    def __init__(self):
        unittest.TestSuite.__init__(self, map(WidgetTestCase,
                                              ("testSize",
                                               "testResize")))
                                               

這樣只需要在suite()方法中返回該類的一個例項就可以了:

    def suite():
    return WidgetTestSuite()
    

如果用於測試的類中所有的測試方法都以test開,Python程式設計師甚至可以用PyUnit模組提供的makeSuite()方法來構造一個TestSuite:

    def suite():
    return unittest.makeSuite(WidgetTestCase, "test")
    

在PyUnit測試框架中,TestSuite類可以看成是TestCase類的一個容器,用來對多個測試用例進行組織,這樣多個測試用例可以自動在一次測試中全部完成。事實上,TestSuite除了可以包含TestCase外,也可以包含TestSuite,從而可以構成一個更加龐大的測試用例集:

suite1 = mysuite1.TheTestSuite()
suite2 = mysuite2.TheTestSuite()
alltests = unittest.TestSuite((suite1, suite2))

編寫測試用例(TestCase)並將它們組織成測試用例集(TestSuite)的最終目的只有一個:實施測試並獲得最終結果。PyUnit使用TestRunner類作為測試用例的基本執行環境,來驅動整個單元測試過程。Python開發人員在進行單元測試時一般不直接使用TestRunner類,而是使用其子類TextTestRunner來完成測試,並將測試結果以文字方式顯示出來:

runner = unittest.TextTestRunner()
runner.run(suite)

使用TestRunner來實施測試的例子如例7所示,

    例7. text_runner.py
from widget import Widget
import unittest
# 執行測試的類
class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget()
    def tearDown(self):
        self.widget.dispose()
        self.widget = None
    def testSize(self):
        self.assertEqual(self.widget.getSize(), (40, 40))
    def testResize(self):
        self.widget.resize(100, 100)        
        self.assertEqual(self.widget.getSize(), (100, 100))        
# 測試
if __name__ == "__main__":
    # 構造測試集
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase("testSize"))
    suite.addTest(WidgetTestCase("testResize"))
    
    # 執行測試
    runner = unittest.TextTestRunner()
    runner.run(suite)
    

要執行該單元測試,可以使用如下命令:

[[email protected] code]$ python text_runner.py

執行結果應該如下所示,表明執行了2個測試用例,並且兩者都通過了測試:

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK

如果對資料進行修改,模擬出錯的情形,將會得到如下結果:

.F
==========================================
FAIL: testResize (__main__.WidgetTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "text_runner.py", line 15, in testResize
    self.assertEqual(self.widget.getSize(), (200, 100))
  File "/usr/lib/python2.2/unittest.py", line 286, in failUnlessEqual
    raise self.failureException, \
AssertionError: (100, 100) != (200, 100)
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)

預設情況下,TextTestRunner將結果輸出到sys.stderr上,但如果在建立TextTestRunner類例項時將一個檔案物件傳遞給了建構函式,則輸出結果將被重定向到該檔案中。在Python的互動環境中驅動單元測試時,使用TextTestRunner類是一個不錯的選擇。

PyUnit模組中定義了一個名為main的全域性方法,使用它可以很方便地將一個單元測試模組變成可以直接執行的測試指令碼,main()方法使用TestLoader類來搜尋所有包含在該模組中的測試方法,並自動執行它們。如果Python程式設計師能夠按照約定(以test開頭)來命名所有的測試方法,那就只需要在測試模組的最後加入如下幾行程式碼即可:

    if __name__ == "__main__":
    unittest.main()
    

使用main()方法來實施測試的例子如例8所示,

    例8. main_runner.py
from widget import Widget
import unittest
# 執行測試的類
class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget()
    def tearDown(self):
        self.widget.dispose()
        self.widget = None
    def testSize(self):
        self.assertEqual(self.widget.getSize(), (40, 40))
    def testResize(self):
        self.widget.resize(100, 100)
        self.assertEqual(self.widget.getSize(), (100, 100))   
# 測試
if __name__ == "__main__":
    unittest.main()        
    

要執行該單元測試,可以使用如下命令:

[[email protected] code]$ python main_runner.py

測試類WidgetTestCase中的所有測試方法都將被自動執行,但如果只想執行testSize()方法,可以使用如下命令:

[[email protected] code]$ python main_runner.py WidgetTestCase.testSize

如果在單元測試指令碼中定義了TestSuite,還可以指定要執行的測試集。使用-h引數可以檢視執行該指令碼所有可能用到的引數:

[[email protected] code]$ python main_runner.py -h
為了使單元測試更具親合力,PyUnit軟體包中還提供了一個圖形介面測試指令碼unittestgui.py,將其複製到當前目錄後,可以執行下面的命令來啟動該測試工具,對main_runner.py指令碼中的所有測試用例進行測試:
[[email protected] code]$ python unittestgui.py main_runner

該測試工具動行時的介面如圖1所示:

 
圖1. 圖形測試工具

單擊Start按鈕可以開始執行所有測試用例,測試結果將如圖2所示:

 
圖2 測試結果

使用圖形介面可以更好地進行單元測試,查詢測試結果也更加方便。PyUnit對於沒有通過的測試會進行區分,指明它是失敗(failure)還是錯誤(error),失敗是被assert類方法(如assertEqual)檢查到的預期結果,而錯誤則是由意外情況所引起的。

回頁首

四、小結

測試是保證軟體質量的關鍵,新的軟體開發方法要求程式設計師在編寫程式碼前先編寫測試用例,並在軟體開發過程中不斷地進行單元測試,從而最大限度地減少缺陷(Bug)的產生。軟體單元測試是XP方法的基石,測試框架為程式設計師進行單元測試提供了統一的規範,Python程式設計師可以使用PyUnit作為軟體開發過程中的自動單元測試框架。

相關推薦

python測試框架應用

大型軟體系統的開發是一個很複雜的過程,其中因為人的因素而所產生的錯誤非常多,因此軟體在開發過程必須要有相應的質量保證活動,而軟體測試則是保證質量的關鍵措施。正像軟體熵(software entropy)所描述的那樣:一個程式從設計很好的狀態開始,隨著新的功能不斷地加入,

I2C子系統驅動框架應用 (轉)

sent cal fcm tran 中一 table same print style I2C子系統驅動框架: 應用程序層(app層) ——————————————————————————————————– i2c driver層: 從設備驅動層(TS Senso

Python 測試框架基礎

cti 單元測試 testcase odin self. teardown 添加 imp not in 1. 導入測試對象2. 導入單元測試框架:unittest3. 創建繼承自unittest.TestCase類的測試類,類中必須包含三個方法:初始化、測試、結束  初始化

Spring學習筆記(五):Spring JDBC 框架應用示例

JDBC 框架概述 在使用普通的 JDBC 資料庫時,就會很麻煩的寫不必要的程式碼來處理異常,開啟和關閉資料庫連線等。但 Spring JDBC 框架負責所有的低層細節,從開始開啟連線,準備和執行 SQL 語句,處理異常,處理事務,到最後關閉連線。 所以當從資料庫中獲取資

Python Ast介紹應用

Abstract Syntax Trees即抽象語法樹。Ast是python原始碼到位元組碼的一種中間產物,藉助ast模組可以從語法樹的角度分析原始碼結構。此外,我們不僅可以修改和執行語法樹,還可以將Source生成的語法樹unparse成python原始碼。因此ast給python原始碼檢查、語法分析、修改

Python測試框架doctest

doctest是python自帶的一個模組。本部落格將介紹doctest的兩種使用方式:一種是嵌入到python原始碼中,另外一種是放到一個獨立檔案。 doctest 的概念模型 在python的官方文件中,對doctest是這樣介紹的: doctest模組會搜尋那些看起來像是python互動

JAVA+Maven+TestNG搭建介面測試框架例項

1、配置JDK 2、安裝Eclipse以及TestNG Eclipse下載地址:http://beust.com/eclipse TestNG安裝過程: 線上安裝 輸入網址:http://beust.com/eclipse 線上安裝會比較慢,有的人可能還會連結不上這個地址,所以下面介紹一個離線下載的方

I2C子系統驅動框架應用

I2C子系統驅動框架: 應用程式層(app層) ——————————————————————————————————– i2c driver層: 從裝置驅動層(TS Sensor等) 1. 需要和應用層互動(fo

基於WEB UI介面輕量級測試框架實施方案

http://mp.weixin.qq.com/s?__biz=MjM5OTI2MTQ3OA==&mid=2652177907&idx=4&sn=31e36ba744bb9683b1bbe49039247909&scene=0#wechat_

ApiTesting全鏈路介面自動化測試框架 - 實戰應用

場景一、新增公共配置 我們在做自動化開始的時候,一般有很多公共的環境配置,比如host、token、user等等,如果這些放在用例中,一旦修改,將非常的不便、麻煩(尤其切換環境)。 所以這裡我們提供了apiConfig.yml 檔案用於存放公共介面配置,預設會根據第一個介面資料包,解析儲存 host 和 he

selenium + python自動化測試unittest框架學習(一)selenium原理應用

自動化 網上 下載安裝 src .cn 基礎 client cnblogs pytho unittest框架的學習得益於蟲師的《selenium+python自動化實踐》這一書,該書講得很詳細,大家可以去看下,我也只學到一點點用於工作中,閑暇時記錄下自己所學才能更加印象深刻

python自動化測試(3)- 自動化框架工具

➜ src git:(master) ✗ python basic_demo.py test_isupper (__main__.TestStringMethods) ... init by setUp... FAIL end by tearDown... test_split (__main_

搭建基於Ant+Jmeter+jenkins的自動負載測試框架的若幹問題記錄解決

例如 需要 ras 分享 測試框架 ron jpg 技術 small 1.關於構建時使用的默認build.xml問題 如圖,如果構建腳本build.xml不在workspace根目錄、或者說構建腳本不叫build.xml。那麽需要在高級裏設置Build File選項的路徑

python專項測試——Android App自動化測試框架

selenium sdk str ase val 測試環境 self ron app自動化 1 為什麽需要框架? 代碼混亂,難閱讀; 重復編碼,效率低;需求變化,難維護; 一 自動化實例 1 準備測試的app;準備測試環境;實現腳本; 2 測試app:只有登錄功能

python Ridge 回歸(嶺回歸)的原理應用

原理 blog 得到 one 技術 設置 fun src print 嶺回歸的原理: 首先要了解最小二乘法的回歸原理 設有多重線性回歸模型 y=Xβ+ε ,參數β的最小二乘估計為 當自變量間存在多重共線性,|X‘X|≈0時,設想|X‘X|給加上一個正常數矩陣(k>

python+unittest+xlrd+request搭建API測試框架

類型 string sts 所有 assert 測試結果 workbook .text ict 實現功能 1.可在表格中進行編寫用例 2.自動執行表格中測試用例 3.對響應結果進行深度斷言,可定位預期結果與測試結果的不同值與位置 4.形成HTML格式的測試報告 源碼可在gi

基於python的接口自動化測試框架

urn __name__ exce 文件格式 圖片 XML pla main tex 公司內部的軟件采用B/S架構,大部分是數據的增刪改查,由於還在開發階段,所以UI界面的變化非常快,難以針對UI進行自動化測試,那樣會消耗大量的精力與時間維護自動化腳本。針對此種情況,針對接

python標準庫其它應用

odi 輸入 log image 介紹 http 參數 odin bsp 一: sys模塊的介紹: 程序如下: #coding:utf-8import sysprint sys.argv[0]print sys.argv[1]print sys.argv[2] 打開cmd

開源接口測試框架之公司應用

希望 簡化 div json del class 以及 str png 最近一段時間,筆者一直在和我們公司的研發童鞋,運維童鞋等一起努力來搞我們公司的接口,每晚的那個點我們就開始了我們的工作,由於是後期補做,所以在時間上也是那麽匆忙,閑暇

python nose測試框架全面介紹七--日誌相關

問題分析 odin message handlers 自己 file trac 配置 statement 引: 之前使用nose框架時,一直使用--logging-config的log文件來生成日誌,具體的log配置可見之前python nose測試框架全面介紹四。 但使用