Unittest單元測試框架

一,前言

1,單元測試

軟件測試通常按階段劃分爲:單元測試,集成測試,系統測試。單元測試(unit testing)是指對軟件中的最小可測試單元進行檢查和驗證。 單元測試中單元的含義,單元就是人爲規定的最小的被測功能模塊,如C語言中單元指一個函數,Java裏單元指一個類,圖形化的軟件中能夠指一個窗口或一個菜單等。在實際項目中,單元測試每每由開發人員完成。html

2,單元測試框架

  • 單元測試其實就是構造數據使用一段代碼去測試另外一段代碼,理論上來講,不使用單元測試框架也能進行單元測試。但若是用於單元測試的代碼(即測試用例)增多,在沒有測試框架的狀況下會變得擁擠、不可管理,這個時候引入測試框架就變得尤其重要。python

  • 單元測試框架提供了一種統一的編程模型,能夠將測試定義爲一些簡單的類,這些類中的方法能夠調用但願測試的應用程序代碼。利用單元測試框架,能夠很輕鬆地插入、設置和分解有關測試的功能,能夠直觀方便地管理測試用例。web

  • 主流的單元測試框架,如Java的Junit、TestNg,python的Unittest、Pyunit、Pytest,通用的自動化測試框架Robot Framework等。編程

3,單元測試框架做用

  • 提供用例組織與執行瀏覽器

  • 提供豐富的斷言方法app

  • 提供豐富的日誌與測試結果框架

二,Unittest 測試框架

1,Unittest 簡介

Unittest是Python自帶的單元測試框架,不只適用於單元測試,還可用於Web、Appium、接口自動化測試用例的開發與執行。該測試框架可組織執行測試用例,而且提供豐富的斷言方法,判斷測試用例是否經過,並最終生成測試結果。函數

Unittest官方文檔:https://docs.python.org/3/library/unittest.html單元測試

2,Unittest 核心要素

  • TestCase:即測試用例,Unittest提供testCase類來編寫測試用例,一個TestCase的實例就是一個測試用例。一條測試用例就是一個完整的測試流程,包括測試前準備環境的搭建(setUp),執行測試代碼(run),以及測試後環境的還原(tearDown),經過運行一條測試用例,能夠對某一個問題進行驗證。測試

  • Fixture:即測試固件,用於測試用例環境的搭建和銷燬。在測試步驟執行前須要爲該測試用例準備環境(SetUp),如啓動app或打開瀏覽器,測試步驟執行後須要恢復環境 (TearDown),如關閉app或瀏覽器,這時候就須要用到Fixture,使代碼更簡潔。

  • TestSuite:即測試套件,把須要執行的測試用例集合在一塊兒就是TestSuite。使用TestLoader來加載TestCase到TestSuite中。

  • TextTestRunner:即測試執行器,用於執行測試用例。該模塊中提供run方法執行TestSuite中的測試用例,並返回測試用例的執行結果,如運行的用例總數、用例經過數、用例失敗數。

  • report:即測試報告。unittest框架沒有自帶的用於生成測試報告的模塊或接口,須要使用第三方的擴展模塊HTMLTestRunner。

3,Unittest 斷言

斷言在自動化測試腳本中是很重要的內容,只有設置正確合適的斷言才能獲取正確的測試結果。Unittest框架提供了本身的斷言方法,以下:

斷言方法
判斷內容
assertEqual(a, b) 判斷 a == b
assertNotEqual(a, b) 判斷 a != b
assertTrue(x) 判斷 bool(x) is True
assertFalse(x) 判斷 bool(x) is False
assertIs(a, b) 判斷 a is b
assertIsNot(a, b) 判斷 a is not b
assertIsNone(x) 判斷 x is None
assertIsNotNone(x) 判斷 x is not None
assertIn(a, b) 判斷 a in b
assertNotIn(a, b) 判斷 a not in b
assertIsInstance(a, b) 判斷 isinstance(a, b)
assertNotIsInstance(a, b) 判斷 not isinstance(a, b)

注意

  • 若是斷言成功則該條測試用例經過,斷言失敗則該條測試用例執行失敗,且會拋出AssertionError錯誤。

  • 以上提供的斷言方法中,都有一個msg參數,默認爲None。若是msg參數有對應的值,則斷言失敗後該msg的值會做爲失敗信息返回,如 assertEqual(a, b, msg="a與b不相等!") 。

三,Unittest 框架使用方法

1,測試需求

測試對象:構造一個類Math,其中包含整數的加、減法運算。

calculator.py

class Math():
    def __init__(self, a, b):
        self.a = int(a)
        self.b = int(b)

    def sum(self):
        '''和'''
        return self.a + self.b

    def sub(self):
        '''差'''
        return self.a - self.b

測試需求:對Math類進行單元測試。接下來針對這個測試需求,使用unittest框架編寫測試用例。

項目目錄結構:後面的例子中,項目結構以下所示。

2,編寫TestCase(測試用例)

在Unittest框架下建立測試用例,步驟以下:

  • 1),導入unittest模塊。

  • 2),建立測試類。測試類的命名不作要求,但須要繼承unittest.TestCase類

  • 3),添加setUp()、tearDown()函數,即測試固件。固然還有setUpClass()、tearDownClass() 函數,區別後面會有介紹。

  • 4),定義測試方法,即測試用例。測試方法名稱必須以test開頭,不然測試時該方法將不會被執行。測試方法裏須要添加斷言。

  • 5),調試執行測試用例。執行當前模塊的測試用例時,調用unittest.main()方法,該方法會搜索該模塊下全部以test開頭的測試用例方法,並執行。其餘方法後面介紹。

針對測試需求,編寫測試用例。目錄結構以下:

test_sum.py

import unittest
from calculator import Math

class SumTest(unittest.TestCase):
    '''測試Math類中的sum函數'''
    def setUp(self):
        print("開始執行測試用例{}...".format(self))

    def test_sum01(self):
        m = Math(3, 4)
        self.assertEqual(m.sum(), 7)

    def test_sum02(self):
        m = Math(2, 8)
        self.assertEqual(m.sum(), 11)

    def tearDown(self):
        print("測試用例{}執行結束...".format(self))

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

運行結果:

.F
======================================================================
FAIL: test_sum02 (__main__.SumTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:/Users/xiaoqq/Desktop/test_project/demo/testSum.py", line 15, in test_sum02
    self.assertEqual(m.sum(), 11)
AssertionError: 10 != 11

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
開始執行測試用例test_sum01 (__main__.SumTest)...
測試用例test_sum01 (__main__.SumTest)執行結束...
開始執行測試用例test_sum02 (__main__.SumTest)...
測試用例test_sum02 (__main__.SumTest)執行結束...

Process finished with exit code 0

結果顯示:

  • test_sum01經過,test_sum02失敗。點"."表示經過,"F"表示失敗。

  • 測試類中每一個測試方法(即測試用例)執行前,都先執行setUp()方法,每一個測試方法執行完畢後都要執行tearDown()方法。

  • 斷言失敗會返回一個AssertionError。

3,在測試用例中添加Fixture(測試夾具)

3.1,測試夾具Fixture的做用對象

在Unittest框架下的測試用例中,使用Fixture有兩種方法,做用於兩個範圍:

  • setUp()、tearDown(),做用於測試方法。即測試類下的每一個測試方法執行前先運行setUp(),每一個測試方法執行完畢後都要執行tearDown()方法,如testSum.py示例。

  • setUpClass()、tearDownClass(),做用於測試類。即只在整個測試類執行開始時運行setUpClass(),測試類下全部測試方法執行完後運行tearDownClass()。

3.2,setUpClass()、tearDownClass() 舉例

test_sum.py修改以下,運行

class SumTest(unittest.TestCase):
    '''測試Math類中的sum函數'''
    @classmethod
    def setUpClass(cls):
        print("開始執行測試用例{}...".format(cls))

    def test_sum01(self):
        m = Math(3, 4)
        self.assertEqual(m.sum(), 7)

    def test_sum02(self):
        m = Math(2, 8)
        self.assertEqual(m.sum(), 11)

    @classmethod
    def tearDownClass(cls):
        print("測試用例{}執行結束...".format(cls))

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

運行結果:

開始執行測試用例<class '__main__.SumTest'>...
測試用例<class '__main__.SumTest'>執行結束...
.F

結果顯示,setUpClass()、tearDownClass() 都只運行了一次。注意,這裏須要使用裝飾器@classmethod

3.3,注意

  • 在測試用例中,setUp() 或 setUpClass() 作初始化的工做,不是必須有這個函數。一樣tearDown() 和 tearDownClass() 只作清理的工做,在測試類中不是必需要有。

  • 須要測試的Math類,代碼比較簡單,沒有真正須要用到測試夾具的地方,所以這只是個用法演示。

  • 實際自動化過程當中,如Web端UI自動化,通常會將建立瀏覽器實例放在setUp() ,用例執行完後須要關閉瀏覽器,將關閉瀏覽器操做放在tearDown()方法裏。示例以下:

import unittest
from selenium import webdriver

class BaiduTest(unittest.TestCase):
    def setUp(self):
        '''打開瀏覽器,進入百度頁面'''
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get('https://www.baidu.com')

    def test_01(self):
        print("操做步驟")

    def tearDown(self):
        '''關閉瀏覽器'''
        self.driver.quit()

4,將測試用例添加至TestSuite(測試套件)

testSum.py模塊中,使用了unittest.main()方法執行當前模塊裏的測試用例。除此以外,Unittest還能夠經過測試套件構造測試用例集,再執行測試用例。構造TestSuite經常使用的方法以下:

4.1,方法一:加載測試用例

  • 1),先經過unittest.TestSuite() 建立測試套件實例對象。

  • 2),再經過addTest() 往測試套件裏添加單個測試用例,或經過addTests([...]) 添加多個測試用例(列表中爲用例方法名)。

  • 3),執行測試套件裏的測試用例

run.py示例:

import unittest
# 導入測試用例模塊
from testcase.test_sum import TestDemo

# 第一步:建立TestSuite實例
suite = unittest.TestSuite()

# 第二步:將測試用例添加至TestSuite
# 方式1,添加單條測試用例
suite.addTest(TestDemo('test_sum01'))   # addTest()裏參數格式爲:測試類('測試方法')
suite.addTest(TestDemo('test_sum02'))

# 方式2,添加多條測試用例
suite.addTests([TestDemo('test_sum01'), TestDemo('test_sum02')])

4.2,方法二:加載測試用例類

  • 1),先經過unittest.TestSuite() 建立測試套件實例對象。

  • 2),再經過unittest.TestLoader()建立加載對象,加載測試用例類

run.py示例:

import unittest
# 導入測試用例模塊
from testcase.test_sum import TestDemo

# 建立TestSuite實例
suite = unittest.TestSuite()
# 建立一個加載對象
loader = unittest.TestLoader()
suite.addTest(loader.loadTestsFromTestCase(TestDemo))

4.3,方法三:加載指定路徑裏的測試用例

  • 1),經過unittest.defaultTestLoader.discover()將指定路徑的測試用例加載至測試用例集。注意:這裏不須要建立unittest.TestSuite對象

  • 2),以下代碼所示,test_dir爲指定路徑。pattern=test_*.py 表示加載以test_開頭的模塊中的測試用例,指定運行某模塊時pattern參數指定全名便可,如:pattern='test_sum.py'。路徑跟pattern參數均可以自定義。

run.py示例

import unittest

test_dir = './testcase'
suite = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')

編寫測試項目時,推薦使用方法三。固然還有其餘方法,很少作介紹。

5,使用TextTestRunner執行測試用例

unittest框架執行測試用例以前,需先建立TextTestRunner實例,再調用該實例的run()方法。

# 建立TextTestRunner實例
runner = unittest.TextTestRunner()
# 使用run()方法運行測試套件(即運行測試套件中的全部用例)
runner.run(suite)

run.py修改爲以下示例:

import unittest

test_dir = './testcase'
suite = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')

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

運行run.py,結果以下:

.F
======================================================================
FAIL: test_sum02 (test_sum.TestDemo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\xiaoqq\Desktop\test_project\demo\test_sum.py", line 15, in test_sum02
    self.assertEqual(m.sum(), 11)
AssertionError: 10 != 11

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)
開始執行測試用例test_sum01 (test_sum.TestDemo)...
測試用例test_sum01 (test_sum.TestDemo)執行結束...
開始執行測試用例test_sum02 (test_sum.TestDemo)...
測試用例test_sum02 (test_sum.TestDemo)執行結束...

Process finished with exit code 0

四,輸出測試報告

unittest框架執行測試用例完成後會在控制檯輸出如上的結果,但實際測試過程當中,咱們須要輸出測試報告,這個時候咱們須要使用第三方模塊。

1,HTMLTestRunner

HTMLTestRunner模塊能夠直接生成html格式的報告

  • 下載地址:http://tungwaiyip.info/software/HTMLTestRunner.html

  • 下載後須要修改:

    • 94行引入的名稱要改,從 import StringIO修改爲 import io
    • 539行 self.outputBuffer = StringIO.StringIO()修改爲self.outputBuffer=io.StringIO()
    • 631行 print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)修改爲print (sys.stderr, '\nTime Elapsed: %s' %(self.stopTime-self.startTime))
    • 642行,if not rmap.has_key(cls): 修改爲 if not cls in rmap:
    • 766行的uo = o.decode('latin-1'),修改爲 uo=o
    • 772行,把 ue = e.decode('latin-1') 直接改爲 ue = e
  • 存放路徑:將修改完成的模塊存放在Python路徑下Lib目錄裏便可。

run.py示例代碼以下:

# -*- coding:utf-8 -*-
# @author: 給你一頁白紙

import time
import unittest
import HTMLTestRunner

# 獲取當前時間並指定時間格式,用於測試報告命名
now = time.strftime("%Y-%m-%d_%H_%M_%S")
# 測試報告存儲路徑
report_dir = './report/'
# 建立報告文件,並以寫的形式打開文件,用於寫入報告內容
fp = open(report_dir + now + "_report.html", 'wb')
# 初始化一個HTMLTestRunner實例對象,用來生成報告
runner = HTMLTestRunner.HTMLTestRunner(stream=fp,
                                       title="App自動化測試報告",
                                       description="測試用例狀況")

# 定義測試用例路徑
test_dir='./testcase'
# 加載測試用例
suite = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
# 執行測試用例
runner.run(suite)
fp.close()

運行run.py,會看到report中生成了html文件,即爲測試報告

瀏覽器打開該文件,內容以下:

從報告內容中看出HTMLTestRunner.HTMLTestRunner()方法中參數所對應的內容,能夠根據項目的實際須要指定參數內容。

2,美化版測試報告

在HTMLTestRunner基礎上美化過的報告

run.py示例代碼以下:

# -*- coding:utf-8 -*-
# @author: 給你一頁白紙

import time
import unittest
import BSTestRunner

# 獲取當前時間並指定時間格式,用於測試報告命名
now = time.strftime("%Y-%m-%d_%H_%M_%S")
# 測試報告存儲路徑
report_dir = './report/'
# 建立報告文件
fp = open(report_dir + now + "_report.html", 'wb')
# 初始化一個HTMLTestRunner實例對象,用來生成報告
runner = BSTestRunner.BSTestRunner(stream=fp,
                                       title="App自動化測試報告",
                                       description="測試用例狀況")

# 定義測試用例路徑
test_dir='./testcase'
# 加載測試用例
suite = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
# 執行測試用例
runner.run(suite)
fp.close()

生成報告樣式以下:

兩種報告模板可根據本身喜愛任意選擇。

相關文章
相關標籤/搜索