python之ddt模塊使用


 

一、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
 


免責聲明!

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



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