python 單元測試(unittest)


自動化測試在各大互聯網公司全面鋪開,那么針對於自動化測試好的設計思想有哪些呢?.....今天我們共同探討下Unittest之數據驅動(DDT是 “Data-Driven Tests”的縮寫)。

對於接口自動化的數據驅動模式是大多數公司所選擇的主流設計思想,有通過Mysql實現數據驅動,有通過Excel實現數據驅動,但是客觀的認為,都沒有Python模塊中DDT模塊所做的數據驅動方便,靈活。測試人員可以編寫腳本進行自動化測試工作和接口回歸測試,開發人員也可以進行提測之前的自測工作,保證代碼質量。

什么是數據驅動呢?

數據驅動就是相同的測試腳本使用不同的測試數據來執行,測試數據和測試行為完全分離,這樣的測試腳本設計模式稱為數據驅動。例如,測試網站的登錄功能,自動化測試工程師想驗證不用的用戶名和密碼在網站登錄時對系統影響結果,就可以使用數據驅動模式來進行自動化測試

 

一.安裝ddt模塊

unittest是python自帶的單元測試框架所以不用安裝但是由於ddt不是Python的標准庫所以我們需要pip安裝ddt模塊(注:如果Python的Scripts目錄已增加到環境變量,請忽略下方操作,直接pip3 install ddt安裝即可。)

 

二.使用pycharm創建unittest文件

 

 

自動生成文件如下

import unittest



#生成一個測試類(繼承unittest.TestCase這個測試類)
class MyTestCase(unittest.TestCase):
    def test_something(self):
       #結果斷言        
        self.assertEqual(True, False)


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

 

三.單元測試用例書寫方法

例子1:

import unittest
#請求方法
import requests


class MyTestCase(unittest.TestCase):

    #每次方法執行之前執行
    def setUp(self):
        print('==============起始============')

    #執行測試的函數 注意:所有執行測試的方法必須以test開頭,執行順序以后面的羅馬數字升序執行
    def test_01(self):
        print('這是第一個用例')
        #斷言 True 等於 True
        self.assertEqual(True, True)

    def test_02(self):
        print('這是第二個用例')
        #斷言 True 等於 True
        self.assertEqual(True, True)

    #每次方法執行之后執行
    def tearDown(self):
        print('==============結束============')


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

運行結果如下;

分析如下:

def setUp(self) 和 def tearDown(self)是unittest的內置方法,他們的意思是在執行用例的前后都被執行一次,每個用例在執行是都會被調用,相當於前置與后置處理方便我們自定義添加固定的方法,適用場景:請求前需要將加入時間戳是實時數據,請求后需要記錄某個參數數據

例子2:
import unittest


class MyTestCase(unittest.TestCase):

  #必須裝飾這個類 @classmethod
def setUpClass(cls): print('最初執行一次') def test_01(self): print('這是用例1') self.assertEqual(True, True) def test_02(self): print('這是用例2') self.assertEqual(True, True) @classmethod def tearDownClass(cls): print('最后執行一次') if __name__ == '__main__': unittest.main()

運行結果如下:

 

分析如下:

def setUpClass(cls): 和 def tearDownClass(cls):同樣也是unittest內置方法但要注意的是需要加上:@classmethod來進行裝飾,它們的意思是在執行用例是只有最初和
最后會被執行,適用場景是:例如登錄接口會返回一個token,將這個token提取出來放在header中,這樣其他接口才可以正常訪問

例子三:將unittest與requests模塊結合 進行接口測試
1.將requests的post請求與get請求封裝成一個類,便於在unittest中進行調用
import requests
import json


class RunMain(object):
    def get(self, url, data):
        res = requests.get(url=url, data=data).json()
        return res

    def post(self, url, data):
        res = requests.post(url=url, data=data).json()
        return res

    def run_main(self, url, method, data=None):
        res = None
     #不區分大小寫
if method.lower() == 'GET': res = self.get(url, data) else: res = self.post(url, data) return res

 

2.unittest中代碼如下
import unittest

#導入請求類
from basis import method


class MyTestCase(unittest.TestCase):

    def setUp(self):
        #初始化請求類
        self.run=method.RunMain()

    def test_01(self):
        '''
        查詢紅包余額接口
        get請求方法
        :return:
        '''
        print('這是第一個用例')
        url='http://ios.wecash.net/biz/wallet/amount'
        data='CUSTOMER_ID=56256A951F81F0BCA10780AD02139B29'
        rep=self.run.get(url,data)
        print(rep)
        # if rep['successful'] == 1:
        #     print('通過')
        # else:
        #     print('沒通過')


        #對返回結果進行斷言判斷(內置斷言)  提取的值,斷言的值,匹配不上的輸出信息
        self.assertEqual(rep['successful'],1, '查詢紅包余額接口失敗')
        # self.assertEqual()驗證兩個是否相等
        # self.assertNotEqual() 判斷不想等
        # self.assertTrue()布爾類型判斷 如果返回是True == True

    def test_02(self):
        '''
        豆瓣API,發送一條廣播
        :return:
        '''
        print('這是第二個用例')
        url='https://api.douban.com/shuo/statuses/'
        data="{'media': [{'imgsrc': 'http://icanhascheezburger.files.wordpress.com/2009/04/funny-pictures-hairless-cat-phones-    home.jpg', 'src': 'http://www.mapsofwar.com/photos/EMPIRE17.swf', 'type': 'flash'}]}"
        rep=self.run.post(url,data)
        print(rep)
        self.assertEqual(rep['msg'],'需要登錄','發送廣播失敗')


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

運行結果如下:

分析如下:

我們在setUp中初始化RunMain()便於在用例中引用,將url與data必填參數傳參,然后通過self.assertEqual()對結果進行結果斷言

 

例子4:探索unittest中接口用例如何上下參數關聯

我們來想一想 如果下面的用例需要上面的用例的返回值 我們怎么做?

import unittest

#導入請求類
from basis import method


class MyTestCase(unittest.TestCase):

    def setUp(self):
        #初始化請求類
        self.run=method.RunMain()
        self.successful=self.test_01()

    def test_01(self):
        '''
        查詢紅包余額接口
        get請求方法
        :return:
        '''
        print('這是第一個用例')
        url='http://ios.wecash.net/biz/wallet/amount'
        data='CUSTOMER_ID=56256A951F81F0BCA10780AD02139B29'
        rep=self.run.get(url,data)
        print(rep)
        # if rep['successful'] == 1:
        #     print('通過')
        # else:
        #     print('沒通過')


        #對返回結果進行斷言判斷(內置斷言)  提取的值,斷言的值,匹配不上的輸出信息
        self.assertEqual(rep['successful'],1, '查詢紅包余額接口失敗')
        return rep['successful']
        # self.assertEqual()驗證兩個是否相等
        # self.assertNotEqual() 判斷不想等
        # self.assertTrue()布爾類型判斷 如果返回是True == True

    def test_02(self):
        '''
        豆瓣API,發送一條廣播
        :return:
        '''
        print('這是第二個用例')
        print(self.successful)
        url='https://api.douban.com/shuo/statuses/'
        data="{'media': [{'imgsrc': 'http://icanhascheezburger.files.wordpress.com/2009/04/funny-pictures-hairless-cat-phones-    home.jpg', 'src': 'http://www.mapsofwar.com/photos/EMPIRE17.swf', 'type': 'flash'}]}"
        rep=self.run.post(url,data)
        print(rep)
        self.assertEqual(rep['msg'],'需要登錄','發送廣播失敗')


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

結果如下:

分析如下:

我們將想要提取的successful 在用例一的地方return出來 在從setup中實例化,這樣做我們確實可以得到successful,但是我們需要重復的調用,因為每一個用例都要走setUp(),很顯然這樣是浪費資源的,所以我們要引入新的知識點:全局變量 globals(),注意:在unittest中用例的執行順序是按照函數名的羅馬數字順序執行的

 

 

import unittest

#導入請求類
from basis import method


class MyTestCase(unittest.TestCase):



    def setUp(self):
        #初始化請求類
        self.run=method.RunMain()
        # self.successful=self.test_01()

    def test_01(self):
        '''
        查詢紅包余額接口
        get請求方法
        :return:
        '''
        print('這是第一個用例')
        url='http://ios.wecash.net/biz/wallet/amount'
        data='CUSTOMER_ID=56256A951F81F0BCA10780AD02139B29'
        rep=self.run.get(url,data)
        print(rep)
        # if rep['successful'] == 1:
        #     print('通過')
        # else:
        #     print('沒通過')


        #對返回結果進行斷言判斷(內置斷言)  提取的值,斷言的值,匹配不上的輸出信息
        self.assertEqual(rep['successful'],1, '查詢紅包余額接口失敗')
        #將successful放入全局變量
        globals()['successful'] =rep['successful']
        # return rep['successful']
        # self.assertEqual()驗證兩個是否相等
        # self.assertNotEqual() 判斷不想等
        # self.assertTrue()布爾類型判斷 如果返回是True == True

    def test_02(self):
        '''
        豆瓣API,發送一條廣播
        :return:
        '''
        print('這是第二個用例')
        #從全局變量中獲取
        print(globals()['successful'])
        url='https://api.douban.com/shuo/statuses/'
        data="{'media': [{'imgsrc': 'http://icanhascheezburger.files.wordpress.com/2009/04/funny-pictures-hairless-cat-phones-    home.jpg', 'src': 'http://www.mapsofwar.com/photos/EMPIRE17.swf', 'type': 'flash'}]}"
        rep=self.run.post(url,data)
        print(rep)
        self.assertEqual(rep['msg'],'需要登錄','發送廣播失敗')


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

結果如下:

四.通過unittest管理用例

例子1:當我們有很多用例時,我們想要跳過一些case去執行,我們怎么去做呢?

當然我們可以直接把這個用例注釋掉 但是這樣太low啦,所以這就引入了新的知識點:@unittest.skip('test_02'),在case上引入這個裝飾類  中間填寫的就是這個case

的名稱

代碼如下:

import unittest

#導入請求類
from basis import method


class MyTestCase(unittest.TestCase):



    def setUp(self):
        #初始化請求類
        self.run=method.RunMain()
        # self.successful=self.test_01()

    def test_01(self):
        '''
        查詢紅包余額接口
        get請求方法
        :return:
        '''
        print('這是第一個用例')
        url='http://ios.wecash.net/biz/wallet/amount'
        data='CUSTOMER_ID=56256A951F81F0BCA10780AD02139B29'
        rep=self.run.get(url,data)
        print(rep)
        # if rep['successful'] == 1:
        #     print('通過')
        # else:
        #     print('沒通過')


        #對返回結果進行斷言判斷(內置斷言)  提取的值,斷言的值,匹配不上的輸出信息
        self.assertEqual(rep['successful'],1, '查詢紅包余額接口失敗')
        #將successful放入全局變量
        globals()['successful'] =rep['successful']
        # return rep['successful']
        # self.assertEqual()驗證兩個是否相等
        # self.assertNotEqual() 判斷不想等
        # self.assertTrue()布爾類型判斷 如果返回是True == True

    @unittest.skip('test_02')
    def test_02(self):
        '''
        豆瓣API,發送一條廣播
        :return:
        '''
        print('這是第二個用例')
        #從全局變量中獲取
        print(globals()['successful'])
        url='https://api.douban.com/shuo/statuses/'
        data="{'media': [{'imgsrc': 'http://icanhascheezburger.files.wordpress.com/2009/04/funny-pictures-hairless-cat-phones-    home.jpg', 'src': 'http://www.mapsofwar.com/photos/EMPIRE17.swf', 'type': 'flash'}]}"
        rep=self.run.post(url,data)
        print(rep)
        self.assertEqual(rep['msg'],'需要登錄','發送廣播失敗')


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

結果如下:

 

例子2:我們現在執行程序還是通過unittest.main(),來執行MyTestCase下面的所有的用例,那么有沒有別的方式來執行用例呢?

答案當然是有的,但是這種方式與main()方式來執行有什么區別呢?

#創建一個放用例的容器
suite=unittest.TestSuite()
#需要往這個容器里面去添加case
suite.addTest(MyTestCase('test_01'))
suite.addTest(MyTestCase('test_02'))
#執行(將我們的容器放進去)
unittest.TextTestRunner.run(suite)

 

例子3:我們在書寫用例的時候並不是一個人在寫,項目組的小伙伴們也在寫,不可能吧所有用例都寫在一個py文件里,所以我們要引入一個新的知識點:

testLoader

代碼如下:

from API.method_4_double import MyTestCase as MyTestCase2
# 此用法可以同時測試多個類
suite1 = unittest.TestLoader().loadTestsFromTestCase(MyTestCase)
suite2 = unittest.TestLoader().loadTestsFromTestCase(MyTestCase2)
suite = unittest.TestSuite([suite1, suite2])
unittest.TextTestRunner(verbosity=2).run(suite)

結果如下:

 下面針對上述腳本中應用到的unittest模塊下的幾個成員進行簡單的介紹,以便於理解上述代碼:
 TestCase:所有測試用例的基本類,給一個測試方法的名字,就會返回一個測試用例實例;
 TestSuit:組織測試用例的實例,支持測試用例的添加和刪除,最終將傳遞給  testRunner進行測試執行;
 TextTestRunner:進行測試用例執行的實例,其中Text的意思是以文本形式顯示測試結果。測試的結果會保存到TextTestResult實例中,包括運行了多少測試用例,成功了多少,失敗了多少等信息;
 TestLoader:用來加載TestCase到TestSuite中的,其中有幾個  loadTestsFrom__()方法,就是從各個地方尋找TestCase,創建它們的實例,然后add到TestSuite中,再返回一個TestSuite實例;

五.使用unittest和HTMLTestRunner結合生成測試報告

我們現在查看測試結果都是通過pycharm中的控制台來查看,這樣的查看方式只能是測試開發人員而普通的功能測試人員不能隨時的查看結果也不方法我們發送測試報告,所以我們要將我們的測試結果生成一個html頁面方便查看與發送測試報告以便將測試結果更加直觀的展示出來

 

下載網上一個開源的HTMLTestRunner.py文件,這個是生成報告的模板文件

下載地址:https://www.cnblogs.com/feiquan/p/8525903.html

重點:當我們下載下來的時候 我們要辦這個文件放在python安裝目錄

/Users/wangsen/miniconda3/lib/python3.5/site-packages/HTMLTestRunner.py 這樣就大功告成了

 現在我們來使用HTMLTestRunner來生成測試報告

代碼如下:

 
         
import unittest
from API import method_5
import HTMLTestRunner
################以一個類的維度控制測試用例的執行#############
cases=unittest.TestLoader().loadTestsFromTestCase(method_5.MyTestCase)
mysuite=unittest.TestSuite([cases])
filename = '/Users/wangsen/PycharmProjects/lufei_learn/report/test.html'
#一二進制方式打開文件,准備寫
file_object=open(filename,'wb')
#使用HTMLTestRunner配置參數,輸出報告路徑,報告標題,描述,均可以配置
runner=HTMLTestRunner.HTMLTestRunner(
stream=file_object,
title='報告主題:接口測試報告',
description='報告詳細描述',

)
#運行測試集合
runner.run(mysuite)
 

結果如下:

7.DDT數據驅動執行接口測試

 

使用數據驅動框架的意義:

 

- 代碼復用率高。同一測試邏輯編寫一次,可以被多條測試數據復用,提高了測試代碼的復用率,同時可以提高測試腳本的編寫效率。
- 異常排查效率高。測試框架依據測試數據,每條數據生成一條測試用例,用例執行過程相互隔離,在其中一條失敗的情況下,不會影響其他的測試用例。
- 代碼的可維護性高。清晰的測試框架,利於其他測試工程師閱讀,提高了代碼的可維護性。

1.以元組, 列表,字典傳遞數據

好了 ,上代碼:

import unittest
#導入ddt的模塊
from ddt import ddt,data,unpack
from basis import method

#用ddt來修飾我們的類
@ddt
class MyTestCase(unittest.TestCase):

    def setUp(self):
        self.run=method.RunMain()

    #引入data來修飾我們的數據,參數化數據
    @data(('查詢紅包余額接口','get','http://ios.wecash.net/biz/wallet/amount','CUSTOMER_ID=56256A951F81F0BCA10780AD02139B29','successful',1),
              ('發送一條廣播','post','https://api.douban.com/shuo/statuses/',"{'media': [{'imgsrc': 'http://icanhascheezburger.files.wordpress.com/2009/04/funny-pictures-hairless-cat-phones-    home.jpg', 'src': 'http://www.mapsofwar.com/photos/EMPIRE17.swf', 'type': 'flash'}]}",'msg','需要登錄'))


    #導入參數
    @unpack
    def test_something(self,name,method,url,data,assertion,value):
        print('測試接口:%s'%name)
        rep=self.run.run_main(url,method,data)

        print(rep)
        self.assertEqual(rep[str(assertion)],value,'測試不通過')




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

代碼解析:

如果我們要引用ddt實現數據驅動 首先需要導入模塊from ddt import ddt,data,unpack

 @ddt是用來是用來裝飾unittest類的

傳入元組、字典、列表等復雜結構數據,@data 方法結合 @unpack方法使用

2.以文件作為數據傳遞@file_data

代碼如下

import unittest
from basis import method
from ddt import ddt,file_data

#裝飾ddt
@ddt
class MyTestCase(unittest.TestCase):

    def setUp(self):
        self.run=method.RunMain()

    #導入file_data()獲取json中的數據,在test_something測試函數中接受
    @file_data("test_data_list.json")
    def test_something(self,message):
        name, method, url, data, assertion, value=message[0],message[1],message[2],message[3],message[4],message[5]

        print("測試接口為:%s"%name)

        rep=self.run.run_main(url,method,data)
        print(rep)
        self.assertEqual(rep[assertion],value,'測試不通過')



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

 

json文件格式如下

[
  ["查詢紅包余額接口","get","http://ios.wecash.net/biz/wallet/amount","CUSTOMER_ID=56256A951F81F0BCA10780AD02139B29","successful",1],
  ["發送一條廣播","post","https://api.douban.com/shuo/statuses/","{'media': [{'imgsrc': 'http://icanhascheezburger.files.wordpress.com/2009/04/funny-pictures-hairless-cat-phones-    home.jpg', 'src': 'http://www.mapsofwar.com/photos/EMPIRE17.swf', 'type': 'flash'}]}","msg","需要登錄"]
]

3.還有最后一種方式 那就是從excel中讀取數據

代碼如下:

import unittest
from basis import method
from ddt import ddt,data,unpack,file_data
from API.ExcelUtil import ParseExcel

excelPath='/Users/wangsen/PycharmProjects/lufei_learn/API/接口自動化測試數據.xlsx'
sheetName='接口數據表'

#創建ParseExcel類的實例對象
excel=ParseExcel(excelPath,sheetName)

@ddt
class MyTestCase(unittest.TestCase):

    def setUp(self):
        self.run=method.RunMain()

    # 導入excel的數據(如果@ddt.data()括號中傳的是一個方法,方法前需要加星號(*))
    @data(*excel.getDatasFromSheet())
    def test_something(self,message):
        name, method, url, data, assertion, value=tuple(message)
        print("測試接口為:%s" % name)
        rep = self.run.run_main(url, method, data)
        print(rep)
        self.assertEqual(rep[assertion], value, '測試不通過')


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

讀取excel數據的代碼如下:

import xlrd
import traceback
class ParseExcel(object):
    #excel路徑,sheet頁名稱
    def __init__(self,excelPath,sheetName):
        try:
            #將讀取得excel加載到內存
            self.wb=xlrd.open_workbook(excelPath)
        except Exception as e:
            print(traceback.format_exc())
        else:
            #通過工作表名稱獲取一個工作表對象
            self.sheet=self.wb.sheet_by_name(sheetName)
            #獲取工作表中存在數據的區域的最大行號
            self.maxRowNum=self.sheet.nrows
    def getDatasFromSheet(self):

        #用於存放從工作表中讀取出來的數據
        dataList=[]
        #因為工作表中的第一行是標題行,所以需要去掉
        for i in range(1,self.maxRowNum):
            #行內容 列表類型
            row = self.sheet.row_values(i)
            if row:
                temList=[]
                temList.append(row[1])
                temList.append(row[2])
                temList.append(row[3])
                temList.append(row[4])
                temList.append(row[5])
                temList.append(row[6])
                dataList.append(temList)

        # print(dataList)
        return dataList

 

 

8.將unittest於Jenkins結合並發送報告郵件(自動化測試項目)

我們已經將unittest如何管理case如何生成報告如何進行數據驅動測試都已經講了,現在講一下如何將unittest變成python自動化測試框架。

本着一切都往高大上走的原則,我們進行如下設計:

開發語言:python

應用模塊:requests

case管理:unittest框架

生成測試報告:HTMLTestRunner

數據如何存儲管理:可以用mysql管理,excel管理,json管理

如何進行測試:Jenkins+unittest進行持續集成

 

問題
如何管理case(如何跳過case,case寫在哪)

如何case執行(如何管理,順序先后不是放在前面就先執行而是根據case命名的升序來執行)

如何解決case的依賴(定義全局變量,也可以放在配置文件里也可以放在數據庫里都是可以的)

如何生成測試報告(放在安裝目錄 lib下面,py2到py3一些修改)

 


免責聲明!

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



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