unittest單元測試框架


簡介:

它主要用於單元測試,還適用於web自動化測試用例的開發與執行,它可以組織執行測試用例,並且提供豐富的斷言方法,判斷測試用例是否通過,最終生成測試結果。

接口測試工具缺點:

1.測試數據不可控制:需要手動向數據庫插入測試數據;
2.無法測試加密接口:遇到對接口參數進行加密/解密的接口或者接口的參數需要使用時間戳,工具很難模擬;
3.擴展性差:如果想生成HTML格式的測試報告或者對接口測試做定時任務,工具難以實現。

接口自動化測試設計:

測試過程:
    1.向測試數據庫插入測試數據(一般使用測試數據庫,不對真實數據庫造成污染);
    2.調用被測系統接口;
    3.接口對數據進行處理,並返回結果;
    4.通過單元測試框架斷言接口返回的數據,並生成測試報告。

unittest模塊的各個屬性說明:

1.TestCase:TestCase類,所有測試用例類繼承的基本類,我們寫的測試類都要繼承它。
2.unittest.main():將一個單元測試模塊變為可直接運行的測試腳本,它主要使用TestLoader類來搜索所有包含在該模塊中以‘test’命名開頭的測試方法,並自動執行他們。執行順序是:按照ASCII碼的順序加載測試用例。
3.TestSuite():用來創建測試套件的。
4.TextTestRunner():是用來執行測試用例的,使用其中的run(suite)方法,來執行suite鎖組裝的測試用例。
5.unittest.skip():裝飾器,可以用來跳過某些不想執行的測試用例.
6.TextTestResult():測試結果會保存到該實例中,包含了運行了多少實例,成功或失敗等信息。

類屬性:

# TestCase類
1.setUp():該方法用於測試用例執行前的初始化工作。例如測試用例中需要訪問數據庫,可以在該方法中建立數據庫連接並進行初始化。
    
2.tearDown():tearDown()方法用於測試用例執行之后的善后工作。如關閉數據庫連接。關閉瀏覽器。

3.assert*():一些斷言方法:在執行測試用例的過程中,最終用例是否執行通過,是通過判斷測試得到的實際結果和預期結果是否相等決定的。

4.assertEqual(a,b,[msg='測試失敗時打印的信息']):斷言a和b是否相等,相等則測試用例通過。

5.assertNotEqual(a,b,[msg='測試失敗時打印的信息']):斷言a和b是否相等,不相等則測試用例通過。

6.assertTrue(x,[msg='測試失敗時打印的信息']):斷言x是否True,是True則測試用例通過。

7.assertFalse(x,[msg='測試失敗時打印的信息']):斷言x是否False,是False則測試用例通過。

8.assertIs(a,b,[msg='測試失敗時打印的信息']):斷言a是否是b,是則測試用例通過。

9.assertNotIs(a,b,[msg='測試失敗時打印的信息']):斷言a是否是b,不是則測試用例通過。

10.assertIsNone(x,[msg='測試失敗時打印的信息']):斷言x是否None,是None則測試用例通過。

11.assertIsNotNone(x,[msg='測試失敗時打印的信息']):斷言x是否None,不是None則測試用例通過。

12.assertIn(a,b,[msg='測試失敗時打印的信息']):斷言a是否在b中,在b中則測試用例通過。

13.assertNotIn(a,b,[msg='測試失敗時打印的信息']):斷言a是否在b中,不在b中則測試用例通過。

14.assertIsInstance(a,b,[msg='測試失敗時打印的信息']):斷言a是是b的一個實例,是則測試用例通過。

15.assertNotIsInstance(a,b,[msg='測試失敗時打印的信息']):斷言a是是b的一個實例,不是則測試用例通過。

# TestSuite類
1.addTest(): addTest()方法是將測試用例添加到測試套件中,如下方,是將test_baidu模塊下的BaiduTest類下的test_baidu測試用例添加到測試套件。
2.addTests(): 可以添加多個測試用例(可迭代的參數)
代碼:
suite = unittest.TestSuite()           suite.addTest(test_baidu.BaiduTest('test_baidu'))

# TextTestRunner類
run(): run()方法是運行測試套件的測試用例,入參為suite測試套件。
代碼:
runner = unittest.TextTestRunner()
runner.run(suite)

三種實現方式:

# coding=utf-8
#1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

#2.注釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2016-7-27
@author: Jennifer
Project:使用unittest框架編寫測試用例思路
'''
#3.導入unittest模塊
import unittest

#4.定義測試類,父類為unittest.TestCase。
#可繼承unittest.TestCase的方法,如setUp和tearDown方法,不過此方法可以在子類重寫,覆蓋父類方法。
#可繼承unittest.TestCase的各種斷言方法。
class Test(unittest.TestCase): 
    
#5.定義setUp()方法用於測試用例執行前的初始化工作。
#注意,所有類中方法的入參為self,定義方法的變量也要“self.變量”
#注意,輸入的值為字符型的需要轉為int型
    def setUp(self):
        self.number=raw_input('Enter a number:')
        self.number=int(self.number)

#6.定義測試用例,以“test_”開頭命名的方法
#注意,方法的入參為self
#可使用unittest.TestCase類下面的各種斷言方法用於對測試結果的判斷
#可定義多個測試用例
#最重要的就是該部分
    def test_case1(self):
        print self.number
        self.assertEqual(self.number,10,msg='Your input is not 10')
        
    def test_case2(self):
        print self.number
        self.assertEqual(self.number,20,msg='Your input is not 20')

    @unittest.skip('暫時跳過用例3的測試')
    def test_case3(self):
        print self.number
        self.assertEqual(self.number,30,msg='Your input is not 30')

#7.定義tearDown()方法用於測試用例執行之后的善后工作。
#注意,方法的入參為self
    def tearDown(self):
        print 'Test over'
        
#8如果直接運行該文件(__name__值為__main__),則執行以下語句,常用於測試腳本是否能夠正常運行
if __name__=='__main__':
#8.1執行測試用例方案一如下:
#unittest.main()方法會搜索該模塊下所有以test開頭的測試用例方法,並自動執行它們。
#執行順序是命名順序:先執行test_case1,再執行test_case2
    unittest.main()

'''
#8.2執行測試用例方案二如下:
#8.2.1先構造測試集
#8.2.1.1實例化測試套件
    suite=unittest.TestSuite()
#8.2.1.2將測試用例加載到測試套件中。
#執行順序是安裝加載順序:先執行test_case2,再執行test_case1
    suite.addTest(Test('test_case2'))
    suite.addTest(Test('test_case1'))
#8.2.2執行測試用例
#8.2.2.1實例化TextTestRunner類
    runner=unittest.TextTestRunner()
#8.2.2.2使用run()方法運行測試套件(即運行測試套件中的所有用例)
    runner.run(suite)
'''
    
'''
#8.3執行測試用例方案三如下:
#8.3.1構造測試集(簡化了方案二中先要創建測試套件然后再依次加載測試用例)
#執行順序同方案一:執行順序是命名順序:先執行test_case1,再執行test_case2
    test_dir = './'
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
#8.3.2執行測試用例
#8.3.2.1實例化TextTestRunner類
    runner=unittest.TextTestRunner()
#8.3.2.2使用run()方法運行測試套件(即運行測試套件中的所有用例)
    runner.run(discover)   
'''

案例:

import unittest
import requests
import os, sys
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, parentdir)
from db_fixture import test_data
 
 
class AddEventTest(unittest.TestCase):
  ''' 添加發布會 '''
 
  def setUp(self):
    self.base_url = "http://127.0.0.1:8000/api/add_event/"
 
  def tearDown(self):
    print(self.result)
 
  def test_add_event_all_null(self):
    ''' 所有參數為空 '''
    payload = {'eid':'','':'','limit':'','address':"",'start_time':''}
    r = requests.post(self.base_url, data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10021)
    self.assertEqual(self.result['message'], 'parameter error')
 
  def test_add_event_eid_exist(self):
    ''' id已經存在 '''
    payload = {'eid':1,'name':'一加4發布會','limit':2000,'address':"深圳寶體",'start_time':'2017'}
    r = requests.post(self.base_url, data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10022)
    self.assertEqual(self.result['message'], 'event id already exists')
 
  def test_add_event_name_exist(self):
    ''' 名稱已經存在 '''
    payload = {'eid':11,'name':'紅米Pro發布會','limit':2000,'address':"深圳寶體",'start_time':'2017'}
    r = requests.post(self.base_url,data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10023)
    self.assertEqual(self.result['message'], 'event name already exists')
 
  def test_add_event_data_type_error(self):
    ''' 日期格式錯誤 '''
    payload = {'eid':11,'name':'一加4手機發布會','limit':2000,'address':"深圳寶體",'start_time':'2017'}
    r = requests.post(self.base_url,data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 10024)
    self.assertIn('start_time format error.', self.result['message'])
 
  def test_add_event_success(self):
    ''' 添加成功 '''
    payload = {'eid':11,'name':'一加4手機發布會','limit':2000,'address':"深圳寶體",'start_time':'2017-05-10 12:00:00'}
    r = requests.post(self.base_url,data=payload)
    self.result = r.json()
    self.assertEqual(self.result['status'], 200)
    self.assertEqual(self.result['message'], 'add event success')
 
 
if __name__ == '__main__':
  test_data.init_data() # 初始化接口測試數據
  unittest.main()

生成HTML測試報告:

當開發的接口達到一定數量后,就需要考慮 分文件分目錄 的來 划分 接口測試用例,如何批量的執行不同文件目錄下的用例呢?unittest單元測試框架提供的 discover() 方法可以幫助我們做到這一點。並使用 HTMLTestRunner 擴展生成 HTML 格式的測試報告。

使用unittest框架所提供的discover()方法,查找 interface/ 目錄下,所有匹配_test.py 的測試文件(星 號匹配任意字符)。

import time, sys
sys.path.append('./interface')
sys.path.append('./db_fixture')
from HTMLTestRunner import HTMLTestRunner
import unittest
from db_fixture import test_data
 
 
# 指定測試用例為當前文件夾下的 interface 目錄
test_dir = './interface'
discover = unittest.defaultTestLoader.discover(test_dir, pattern='*_test.py')
 
 
if __name__ == "__main__":
  test_data.init_data() # 初始化接口測試數據
 
  now = time.strftime("%Y-%m-%d %H_%M_%S")
  filename = './report/' + now + '_result.html'
  fp = open(filename, 'wb')
  runner = HTMLTestRunner(stream=fp,
              title='Guest Manage System Interface Test Report',
              description='Implementation Example with: ')
  runner.run(discover)
  fp.close()

HTMLTestRunner 為unittest單元測試框架的擴展,利用它所提供的HTMLTestRunner()類來替換unittest單元測試框架的TextTestRunner()類,從而生成HTML格式的測試報告。

遺憾的是HTMLTestRunner並不支持Python3.x,大家可以在網上找到適用於Python3.x的HTMLTestRunner.py文件,使用在自己的接口自動化工程中。

通過 time 的 strftime()方法獲取當前時間,並且轉化成一定的時間格式。作為測試報告的名稱。這樣做目的是是為了避免因為生成的報告的名稱重名而造成報告的覆蓋。最終,將測試報告存放於report/目錄下面。如下圖,一張完整的接口自動化測試報告。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM