python之ddt模組使用
阿新 • • 發佈:2020-08-18
一、DDT(資料驅動)簡介
Data-Driven Tests(DDT)即資料驅動測試,可以實現不同資料運行同一個測試用例(通過資料的不同來驅動測試結果的不同)。
ddt本質其實就是裝飾器,一組資料一個場景。
ddt模組包含了一個類的裝飾器ddt(@ddt)和三個方法的裝飾器(@data、@unpack、@file_data),其中:
@data:包含多個你想要傳給測試用例的引數,可以為列表、元組、字典等;
@file_data:會從json或yaml中載入資料;
(注意,如果檔案以”.yml”或者”.yaml”結尾,ddt會作為yaml型別處理,其他所有檔案都會作為json檔案處理。如txt檔案)
@unpack:分割元素。
(需要搭配unittest測試框架使用,實現資料驅動測試)
資料驅動測試:
1、避免編寫重複程式碼
2、資料與測試指令碼分離
3、通過使用資料驅動測試,來驗證多組資料測試場景
通常來說,多用於單元測試和介面測試
二、python中使用ddt傳遞引數 前提:需要安裝ddt包
1、傳遞列表、字典等資料
# get_ddt.py import unittest from ddt import ddt, data, unpack, file_data # 聲明瞭ddt類裝飾器 @ddt class MyddtTest(unittest.TestCase):# @data方法裝飾器 # 單組元素 @data(1,2,3) def test_01(self, value): # value用來接受data的資料 print(value) # 多組資料,未拆分 @data([1,2],[3,4]) def test_02(self, value): print(value) # 多組資料,拆分 # @unpac拆分,相當於把資料的最外層結構去掉 @data([5,6],[7,8]) @unpack def test_03(self, value1, value2):print(value1, value2) # 單個列表字典,未拆分 @data([{"name": "peter", "age": 15, "addr": "chengdu"}]) def test_04(self, value): print(value) # 多個列表字典,拆分 @data([{"name":"peter","age":16,"addr":"chengdu"},{"name":"lily","age":17,"addr":"chengdu"}]) @unpack def test_05(self, value1, value2): print(value1, value2) # 單個字典,拆分 # @data裡的資料key必須與字典的key保持一致 @data({"name":"jack","age":20}) @unpack def test_06(self, name, age): print(name, age) # 多個字典, 拆分 @data({"name":"peter","age":18,"addr":"chengdu"},{"name":"lily","age":19,"addr":"chengdu"}) @unpack def test_07(self, name, age, addr): print(name, age, addr) # 多個列表字典,引用資料 testdata = [{"name": "peter", "age": 21, "addr": "chengdu"}, {"name": "lily", "age": 22, "addr": "chengdu"}] @data(testdata) @unpack def test_08(self, value1, value2): print(value1, value2) # @data(*testdata):*號意為解包,ddt會按逗號分隔,將資料拆分(不需要@unpack方法裝飾器了) testdata = [{"name":"peter","age":23,"addr":"chengdu"},{"name":"lily","age":24,"addr":"chengdu"}] @data(*testdata) def test_09(self, value): print(value)if __name__ == "__main__": unittest.main()
執行結果: ................... ---------------------------------------------------------------------- Ran 19 tests in 0.000s OK 1 2 3 [1, 2] [3, 4] 5 6 7 8 [{'name': 'peter', 'age': 15, 'addr': 'chengdu'}] {'name': 'peter', 'age': 16, 'addr': 'chengdu'} {'name': 'lily', 'age': 17, 'addr': 'chengdu'} jack 20 peter 18 chengdu lily 19 chengdu {'name': 'peter', 'age': 21, 'addr': 'chengdu'} {'name': 'lily', 'age': 22, 'addr': 'chengdu'} {'name': 'peter', 'age': 23, 'addr': 'chengdu'} {'name': 'lily', 'age': 24, 'addr': 'chengdu'}
2、傳遞json、yaml檔案
# config.json { "stu1": { "name": "Peter", "age": 29, "addr": "BeiJing" }, "stu2": { "name": "Jack", "age": 30, "addr": "ShenZhen" } }
# config.yaml # 使用-分隔用例,則yaml讀取到的資料型別為列表 - model: 註冊模組 title: 註冊成功 url: http://api.nnzhp.cn/api/user/user_reg method: POST data: username: yingcr10 pwd: Ace123456 cpwd: Ace123456 check: error_code: 0 msg: 註冊成功! - model: 註冊模組 title: 使用者名稱長度小於6位,註冊失敗 url: http://api.nnzhp.cn/api/user/user_reg method: POST data: username: yingc pwd: Ace123456 cpwd: Ace123456 check: error_code: 3002
# get_ddt.py
import unittest from ddt import ddt, data, unpack, file_data # 聲明瞭ddt類裝飾器 @ddt class MyddtTest(unittest.TestCase): # @file_data載入json檔案 # **testdata:將提取到的資料存放在空字典testdata中 @file_data("config.json") def test_10(self, **testdata): # 再從字典testdata中單獨提取引數 name = testdata['name'] age = testdata['age'] addr = testdata['addr'] print(testdata) print(name, age, addr) # 直接提取引數, test()方法中的引數必須與json檔案中的鍵保持一致 @file_data("config.json") def test_11(self,name, age, addr): name = name age = age addr = addr print(name, age, addr) # @file_data載入yaml檔案 @file_data("config.yaml") def test_12(self, model, title, url, method, data, check): username = data['username'] pwd = data['pwd'] cpwd = data['pwd'] print(model, title, url, method, data, check) print(username, pwd, cpwd) # **testdata:將提取到的資料存放在空字典testdata中 @file_data("config.yaml") def test_13(self, **testdata): # 再從字典testdata中單獨提取引數 model = testdata['model'] title = testdata['title'] print(testdata) print(model, title) if __name__ == "__main__": unittest.main()
執行結果: ........ ---------------------------------------------------------------------- Ran 8 tests in 0.000s OK {'name': 'Peter', 'age': 29, 'addr': 'BeiJing'} Peter 29 BeiJing {'name': 'Jack', 'age': 30, 'addr': 'ShenZhen'} Jack 30 ShenZhen Peter 29 BeiJing Jack 30 ShenZhen 註冊模組 註冊成功 http://api.nnzhp.cn/api/user/user_reg POST {'username': 'yingcr10', 'pwd': 'Ace123456', 'cpwd': 'Ace123456'} {'error_code': 0, 'msg': '註冊成功!'} yingcr10 Ace123456 Ace123456 註冊模組 使用者名稱長度小於6位,註冊失敗 http://api.nnzhp.cn/api/user/user_reg POST {'username': 'yingc', 'pwd': 'Ace123456', 'cpwd': 'Ace123456'} {'error_code': 3002} yingc Ace123456 Ace123456 {'model': '註冊模組', 'title': '註冊成功', 'url': 'http://api.nnzhp.cn/api/user/user_reg', 'method': 'POST', 'data': {'username': 'yingcr10', 'pwd': 'Ace123456', 'cpwd': 'Ace123456'}, 'check': {'error_code': 0, 'msg': '註冊成功!'}} 註冊模組 註冊成功 {'model': '註冊模組', 'title': '使用者名稱長度小於6位,註冊失敗', 'url': 'http://api.nnzhp.cn/api/user/user_reg', 'method': 'POST', 'data': {'username': 'yingc', 'pwd': 'Ace123456', 'cpwd': 'Ace123456'}, 'check': {'error_code': 3002}} 註冊模組 使用者名稱長度小於6位,註冊失敗
三、通過ddt讀取yaml測試資料
config.yaml資料檔案與上文的一致。
# get_ddt.py import requests import unittest import json from ddt import ddt, data, unpack, file_data @ddt class SignTest(unittest.TestCase): # 使用ddt載入yaml中的測試資料 @file_data("config.yaml") def test_get_yaml(self,model,title,url,method,data,check): # 提取分離各引數 model = model title = title url = url method = method data = data check = check self.sign_test(model,title,url,method,data,check) def sign_test(self,model,title,url,method,data,check): print("模組: ", model) print("用例標題: ", title) response = requests.request(url=url, method=method, data=data).text response = json.loads(response) try: # 通過斷言,判斷測試是否通過 assert check['error_code'] == response['error_code'] print("測試通過") except Exception as e: print("測試失敗") raise e if __name__ == "__main__": unittest.main()
執行結果: 模組: 註冊模組 用例標題: 註冊成功 測試通過 模組: 註冊模組 用例標題: 使用者名稱長度小於6位,註冊失敗 測試通過 .. ---------------------------------------------------------------------- Ran 2 tests in 0.188s OK
四、python中使用ddt+excel讀取測試資料
大體思路:先從excel檔案中讀取資料,然後再用ddt載入已讀取的資料。
# get_excel.py from openpyxl import load_workbook class ExcelData(): def __init__(self, file="config.xlsx"): ''' 初始化Excel物件 ''' self.file = file self.wb = load_workbook(self.file) def get_row_value(self, row, sheet_name="Sheet1"): ''' 獲取Excel中某一行的資料 ''' sh = self.wb[sheet_name] max_col = sh.max_column row_value = [] for col in range(1, max_col+1): value = sh.cell(row, col).value row_value.append(value) return row_value def get_all_row(self, sheet_name="Sheet1"): ''' 獲取Excel中所有行的資料,並存放在列表中 ''' sh = self.wb[sheet_name] max_row = sh.max_row row_value = [] for row in range(2, max_row+1): value = self.get_row_value(row) row_value.append(value) return row_value if __name__ == "__main__": excel = ExcelData() testdata = excel.get_all_row() print(testdata)
# get_ddt.py import requests import unittest from ddt import ddt, data, unpack, file_data from get_excel import ExcelData @ddt class SignTest(unittest.TestCase): # 從get_excel.py中讀取測試資料 excel = ExcelData() testdata = excel.get_all_row() @data(*testdata) def test_sign(self, datas): # 由於從excel中讀取到的資料為列表形式,所以採用下標來提取各引數 ID = datas[0] model = datas[1] title = datas[2] method = datas[3] url = datas[4] username = datas[5] pwd = datas[6] cpwd = datas[7] check = datas[8] body = { "username": username, "pwd": pwd, "cpwd": cpwd } self.sign_test(ID,model,title,url,method,body,check) def sign_test(self,ID,model,title,url,method,body,check): print("用例ID:", ID) print("模組:", model) print("用例標題:", title) response = requests.request(url=url, method=method, data=body).text try: # 通過斷言,比較實際結果是否與預期結果一致 # 由於從excel中讀取到的check為str型別,所以response不用轉換為dict,直接斷言比較是否相等 assert check == response print("測試通過") except Exception as e: print("測試失敗") raise e if __name__ == "__main__": unittest.main()
執行結果: 用例ID: 001 模組: 註冊模組 用例標題: 正確的使用者名稱和密碼,註冊成功 .測試通過 用例ID: 002 模組: 註冊模組 用例標題: 使用者名稱長度小於6位,註冊失敗 .測試通過 OK ---------------------------------------------------------------------- Ran 2 tests in 0.190s Process finished with exit code 0
參考:https://www.jianshu.com/p/78998bcf3e05
參考:http://www.manongjc.com/detail/18-eosubecmlglohgb.html