【自動化測試】Python - unittest單元測試框架


1、測試模型

下面這部分來自於某書籍資料,拿過來,按需參考一下:html

測試模型
(1)線性測試
一、概念:
經過錄制或編寫對應應用程序的操做步驟產生的線性腳本。單純的來模擬用戶完整的操做場景。(操做,重複操做,數據)都混合在一塊兒。
二、優勢:每一個腳本相對獨立,且不產生其餘依賴和調用。任何一個測試用例腳本拿出來均可以單獨執行。
三、缺點:開發成本高,用例之間存在重複的操做。好比重複的用戶登陸和退出。
維護成本高,因爲重複的操做,當重複的操做發生改變時,則須要逐一進行腳本的修改。
4.線性測試實例:用戶登陸
(2)模塊化驅動測試
一、概念:
將重複的操做獨立成功共模塊,當用例執行過程當中須要用到這一模塊操做時則被調用。
操做+(重複操做,數據)混合在一塊兒。例如,自動化測試的執行須要保持測試用例的獨立性和完整性,因此每一條用例在執行時都須要登陸和退出操做,so能夠把登陸和退出的操做封裝爲公共函數。
二、優勢:因爲最大限度消除了重複,從而提升了開發效率和提升測試用例的可維護性。
三、缺點:雖然模塊化的步驟相同,可是測試數據不一樣。好比說重複的登陸模塊,若是登陸用戶不一樣,依舊要重複編寫登陸腳本。
4.實例:對公共模塊,例如登錄和退出進行模塊化封裝
(3)數據驅動測試
一、概念:它將測試中的測試數據和操做分離,數據存放在另一個文件中單獨維護。
經過數據的改變從而驅動自動化測試的執行,最終引發測試結果的改變。
操做+重複操做+數據分開。
二、優勢:
經過這種方式,將數據和重複操做分開,能夠快速增長類似測試,完成不一樣數據狀況下的測試。
三、實例從excel表格讀取用戶名密碼,登陸郵箱。

2、unittest框架

用Python搭建自動化測試框架,須要組織用例以及測試執行,大部分推薦的是unittest。如今用的也是這個,隨着瞭解,也有其餘的框架,有時間再多去學習,保持持續學習哦~
附上官方文檔地址:連接描述 https://docs.python.org/2.7/l...python

unittest是Python自帶的單元測試框,能夠用來做自動化測試框架的用例組織執行框架。優勢:提供用例組織與執行方法;提供比較方法;提供豐富的日誌、清晰的報告。
大體流程:數據庫

  • 寫好TestCase
  • 由TestLoader加載TestCase到TestSuite
  • 而後由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中。
    經過命令行或者unittest.main()執行時,main會調用TextTestRunner中的run()來執行,或者能夠直接經過TextTestRunner來執行用例。
    在Runner執行時,默認將執行結果輸出到控制檯,咱們能夠設置其輸出到文件,在文件中查看結果。

unittest中最核心的部分是:TestFixture、TestCase、TestSuite、TestRunner。瀏覽器

一、Test fixture

用於一個測試環境的準備和銷燬還原。
當測試用例每次執行以前須要準備測試環境,每次測試完成後還原測試環境,好比執行前鏈接數據庫、打開瀏覽器等,執行完成後須要還原數據庫、關閉瀏覽器等操做。這時候就能夠啓用testfixture。框架

  • setUp():準備環境,執行每一個測試用例的前置條件;
  • tearDown():環境還原,執行每一個測試用例的後置條件;
  • setUpClass():必須使用@classmethod裝飾器,全部case執行的前置條件,只運行一次;
  • tearDownClass():必須使用@classmethod裝飾器,全部case運行完後只運行一次;

例如:less

# 重寫TestCase的setUp() tearDown()方法:在每一個測試方法執行前以及執行後各執行一次
def setUp(self):    # 鉤子方法
    print("do something before test : prepare environment")
def tearDown(self): # 鉤子方法
    print("do something after test : clean up ")

二、TestCase

類,unittest.TestCase
一個類class繼承 unittest.TestCase,就是一個測試用例。一個TestCase的實例就是一個測試用例,就是一個完整的測試流程。
包括測試前環境準備setUp()|setUpClass()、執行代碼run()、測試環境後的還原tearDown()|tearDownClass()。
繼承自unittest.TestCase的類中,測試方法的名稱要以test開頭。且只會執行以test開頭定義的方法(測試用例)。ide

例如:【先準備待測試的方法function.py】模塊化

#!/usr/bin/python3
# -*- coding:utf-8 -*-
def add(a,b):
    return a+b
def minus(a,b):
    return a-b
def multi(a,b):
    return a*b
def divide(a,b):
    return a/b

【測試腳本】:函數

import unittest
from A_UnitTest_basicDemo_ok.function import *

class TestFunc(unittest.TestCase):
    # 繼承自unittest.TestCase
    # 重寫TestCase的setUp()、tearDown()方法:在每一個測試方法執行前以及執行後各執行一次
    def setUp(self):
        print("do something before test : prepare environment")

    def tearDown(self):
        print("do something after test : clean up ")
    
    # 測試方法均已test開頭,不然是不被unittest識別的
    def test_add(self):
        print("add:")
        self.assertEqual(3,add(1,2))

    def test_minus(self):
        print("minus")
        self.assertEqual(3,minus(5,2))

    def test_multi(self):
        print("multi")
        self.assertEqual(6,multi(2 ,3))

    def test_divide(self):
        print("divide")
        self.assertEqual(2,divide(4,2))

if __name__ == "__main__":
    # 在main()中加verbosity參數,能夠控制輸出的錯誤報告的詳細程度
    # verbosity=*:默認是1;設爲0,則不輸出每個用例的執行結果;2-輸出詳細的執行結果
    unittest.main(verbosity=2)

或者也可使用setUpClass() & tearDownClass()方法:單元測試

# 若是想在全部case執行以前準備一次測試環境,並在全部case執行結束後再清理環境
@classmethod
def setUpClass(cls):
    print("this setupclass() method only called once")
@classmethod
def tearDownClass(cls):
    print("this teardownclass() method only called once too")

【verbosity】
在測試用例文件的末尾加上以下代碼:

if __name__ == "__main__":
    unittest.main(verbosity=2)  # 輸出詳細的錯誤報告

在unittest.main()中加參數verbosity能夠控制錯誤報告的詳細程度:默認爲1。0,表示不輸出每個用例的執行結果;2表示詳細的執行報告結果。

【執行結果】:輸出到控制檯

this setupclass() method only called once
test_add (__main__.TestFunc) ... ok
add:
test_divide (__main__.TestFunc) ... ok
divide
test_minus (__main__.TestFunc) ... ok
minus
test_multi (__main__.TestFunc) ... ok
multi

this teardownclass() method only called once too
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

三、TestSuite

上述簡單的測試會產生兩個問題,可不能夠控制test測試用例的執行順序?若不想執行某個測試用例,有沒有辦法能夠跳過?
對於執行順序,默認按照test的 A-Z、a-z的方法執行。若要按本身編寫的用例的前後關係執行,須要用到testSuite。
把多個測試用例集合起來,一塊兒執行,就是testSuite。testsuite還能夠包含testsuite。
通常經過addTest()或者addTests()向suite中添加。case的執行順序與添加到Suite中的順序是一致的。
例如:run_main.py

if __name__ == "__main__":
    suite = unittest.TestSuite()
# 定義list,按照list裏的順序執行測試用例
tests=[TestFunc("test_add"),TestFunc("test_minus"),TestFunc("test_multi"),TestFunc("test_divide")]
suite.addTests(tests)
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)

TestSuite能夠再包含testsuite,示例以下:

suite1 = module.TheTestSuite()
suite2=module.TheTestSuite()
alltests=unittest.TestSuite([suite1],[suite2])

跳過某個case:skip裝飾器

若想讓某個測試用例不執行,有沒有辦法呢?固然是有的,可使用skip裝飾器。
例如:

@unittest.skip("i don't want to run this case -> test_minus() ... ")
def test_minus(self):
    print("minus")
    self.assertEqual(3,minus(5,2))

加上「@unittest.skip()」後,執行看看,對比控制檯的輸出結果就能夠明顯看出區別了。
Skip裝飾器有以下幾種狀況:
(1)skip():無條件跳過
@unittest.skip("i don't want to run this case. ")
(2)skipIf(condition,reason):若是condition爲true,則 skip
@unittest.skipIf(condition,reason)
(3)skipUnless(condition,reason):若是condition爲False,則skip
@unittest.skipUnless(condition,reason)

(4)還可使用TestCase.skipTest(reason)。例如:

def test_divide(self):
    self.skipTest('do not run  test_divide()')
    print("divide")
    self.assertEqual(2,divide(4,2))

控制檯輸出(部分):

test_divide (__main__.TestFunc) ... skipped 'do not run  test_divide()'

四、TestLoader

TestLoadder用來加載TestCase到TestSuite中。
loadTestsFrom*()方法從各個地方尋找testcase,建立實例,而後addTestSuite,再返回一個TestSuite實例。
該類提供如下方法:

unittest.TestLoader().loadTestsFromTestCase(testCaseClass)
unittest.TestLoader().loadTestsFromModule(module)
unittest.TestLoader().loadTestsFromName(name,module=None)
unittest.TestLoader().loadTestsFromNames(names,module=None)
unittest.TestLoader().getTestCaseNames(testCaseclass)
unittest.TestLoader().discover()

TestLoader 之discover:
用discover()來加載測試多個測試用例,再用TextRunner類的run()方法去一次執行多個腳本的用例,達到批量執行的效果。
discover方法裏面有三個參數:
-case_dir:這個是待執行用例的目錄。
-pattern:這個是匹配腳本名稱的規則,test*.py意思是匹配test開頭的全部腳本。
-top_level_dir:這個是頂層目錄的名稱,通常默認等於None就好了。

TextTestRunner():執行測試用例。runner.run(test)會執行TestSuite、TestCase中的run(result)方法。
以下:run_main.py示例

import unittest
import os
# 用例的路徑
case_path = os.path.join(os.getcwd(),"case")
# 報告存放的路徑
report_path = os.path.join(os.getcwd(),"report")
def all_cases():
    discover= unittest.defaultTestLoader.discover(case_path,pattern="test*.py",top_level_dir=None)
    print(discover)
    return discover
if __name__ == "__main__":
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(all_cases())

五、生成測試報告

  • 生成TXT測試報告

代碼示例:

if __name__ == "__main__":
    suite = unittest.TestSuite()
    # 生成.txt的測試報告(控制檯的輸出寫入到文件中)
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFunc))
    with open('UnittestTextReport.txt','a') as f:
        runner = unittest.TextTestRunner(stream=f,verbosity=2)
        runner.run(suite)

能夠看到在目錄下,生成了UnittestTextReport.txt文件。
可是txt格式的文件太過於簡陋。咱們能夠藉助與第三方提供的庫來輸出更加形象的html報告,也能夠自定義輸出本身想要格式的html格式的報告。

  • 生成HTML測試報告
  • 先下載HTMLTestRunner.py(注意Python的版本),
    http://tungwaiyip.info/softwa... 。而後放在Python的Lib目錄下;
  • 在run_main.py文件中加入:from HTMLTestRunner import HTMLTestRunner

HTMLTestRunner()方法有三個參數:
--stream:測試報告寫入文件的存儲區域
--title:測試報告的主題
--description:測試報告的描述

代碼示例:

if __name__ == "__main__":
    suite = unittest.TestSuite()
    # 生成HTML格式的具體測試報告
    with open('HtmlReport.html','wb') as f:  # 在python3,要寫成'wb' or 'wr'
        runner = HTMLTestRunner(stream=f,title='function test   
report',description='generated by HTMLTestRunner',verbosity=2)
        runner.run(suite)

3、代碼示例

  • function.py
#!/usr/bin/python3
# -*- coding:utf-8 -*-
def add(a,b):
    return a+b
def minus(a,b):
    return a-b
def multi(a,b):
    return a*b
def divide(a,b):
    return a/b
  • Test_function.py
#!/usr/bin/python3
# -*- coding:utf-8 -*-
import unittest
from UnitTest_1.function import *  # from..import ... :要指定文件的路徑

class TestFunc(unittest.TestCase): # unittest.TestCase
# 若是想在全部case執行以前準備一次測試環境,並在全部case執行結束後再清理環境
    @classmethod
    def setUpClass(cls):
        print("this setupclass() method only called once")
    @classmethod
    def tearDownClass(cls):
        print("this teardownclass() method only called once too")

    # 測試方法均已test開頭,不然是不被unittest識別的
    def test_add(self):
        print("add:")
        self.assertEqual(3,add(1,2))
    def test_minus(self):
        print("minus")
        self.assertEqual(3,minus(5,2))
    # 若是想臨時跳過某個case:skip裝飾器
    @unittest.skip("i don't want to run this case. ")
    def test_multi(self):
        print("multi")
        self.assertEqual(6,multi(2,3))
    def test_divide(self):
        print("divide")
        self.assertEqual(2,divide(5,2))

if __name__ == "__main__":
    # 在main()中加verbosity參數,能夠控制輸出的錯誤報告的詳細程度
    # verbosity=*:默認是1;設爲0,則不輸出每個用例的執行結果;2-輸出詳細的執行結果
    unittest.main(verbosity=2)
  • Test_suite.py
#!/usr/bin/python3
# -*- coding:utf-8 -*-

import unittest
from UnitTest_1.test_function import TestFunc
from HTMLTestRunner import HTMLTestRunner
# 在Python3中已經沒有 StringIO,因此引用的時候要注意
from io import StringIO

if __name__ == "__main__":
    suite = unittest.TestSuite()
    # 定義list,按照list裏的順序執行測試用例
    tests = [TestFunc("test_add"),TestFunc("test_minus"),TestFunc("test_multi"),TestFunc("test_divide")]
    suite.addTests(tests)
    '''
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)
    '''
    
    # 生成.txt的測試報告(控制檯的輸出寫入到文件中)
    '''
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFunc))
    with open('UnittestTextReport.txt','a') as f:
        runner = unittest.TextTestRunner(stream=f,verbosity=2)
        runner.run(suite)
    '''

    # 生成HTML格式的具體測試報告
    with open('HtmlReport.html','wb') as f:  # 在python3,要寫成'wb' or 'wr'
        runner = HTMLTestRunner(stream=f,title='function test report',description='generated by 
                    HTMLTestRunner',verbosity=2)
        runner.run(suite)

題外話:duang~ duang~ duang~ 週末啦~
腦袋有點疼 肥家肥家 我愛工做 工做愛我


❤ thanks for watching, keep on updating...

相關文章
相關標籤/搜索