一、整體結構
- 框架組成:pytest+requests+allure
- 設計模式:
- - 關鍵字驅動
- 專案結構:
- - 工具層:api_keyword/
- 引數層:params/
- 用例層:case/
- 資料驅動:data_driver/
- 資料層:data/
- 邏輯層:logic/
二、具體步驟及程式碼
1、工具層
將 get、post 等常用行為進行二次封裝。
程式碼(api_key.py)如下:
import allure
import json
import jsonpath
import requests
# 定義一個關鍵字類
class ApiKey:
# 將get請求行為進行封裝
@allure.step("傳送get請求")
def get(self, url, params=None, **kwargs):
return requests.get(url=url, params=params, **kwargs)
# 將post請求行為進行封裝
@allure.step("傳送post請求")
def post(self, url, data=None, **kwargs):
return requests.post(url=url, data=data, **kwargs)
# 由於介面之間可能相互關聯,因此下一個介面需要上一個介面的某個返回值,此處採用jsonpath對上一個介面返回的值進行定位並取值
@allure.step("獲取返回結果字典值")
def get_text(self, data, key):
# json資料轉換為字典
json_data = json.loads(data)
# jsonpath取值
value = jsonpath.jsonpath(json_data, '$..{0}'.format(key))
return value[0]
- 其中引用 allure.step() 裝飾器進行步驟詳細描述,使測試報告更加詳細。
- 使用 jsonpath 對介面的返回值進行取值。
2、資料層
資料採用 yaml 檔案。
程式碼(user.yaml)如下:
-
user:
username: admin
password: '123456'
msg: success
title: 輸入正確賬號、密碼,登入成功
-
user:
username: admin1
password: '1234561'
msg: 使用者名稱或密碼錯誤
title: 輸入錯誤賬號1、密碼1,登入失敗
-
user:
username: admin2
password: '1234562'
msg: 使用者名稱或密碼錯誤
title: 輸入錯誤賬號2、密碼2,登入失敗
- 其中 title 是為了在用例進行時動態獲取引數生成標題。
3、資料驅動層
對資料進行讀寫。
程式碼(yaml.driver.py)如下:
import yaml
def load_yaml(path):
file = open(path, 'r', encoding='utf-8')
data = yaml.load(file, Loader=yaml.FullLoader)
return data
4、引數層
引數層存放公共使用的引數,在使用時對其進行呼叫。
程式碼(allParams.py)如下:
'''python
規則:
全域性變數使用大寫字母表示
'''
# 地址
URL = 'http://39.98.138.157:'
# 埠
PORT = '5000'
5、邏輯層
用例一:進行登入的介面請求,此處登入請求在 yaml 檔案裡設定了三組不同的資料進行請求。
用例二:進行個人查詢的介面請求,此處需要用到登入介面返回的 token 值。
用例三、進行新增商品到購物車的介面請求,此處需要用到登入介面返回的 token 值以及個人查詢介面返回的 openid、userid 值
用例四、進行下單的介面請求,此處需要用到登入介面返回的 token 值以及個人查詢介面返回的 openid、userid、cartid 值
注意:由於多數介面需要用到登入介面返回的 token 值,因此封裝一個 conftest.py 定義專案級前置 fixture,在整個專案只執行一次,可以在各個用例中進行呼叫(其他共用引數也可以採取類似前置定義)。同時由於此處定義的專案級 fixture,因此可以將初始化工具類 ak = ApiKey() 也放入其中。
程式碼(conftest.py)如下:
from random import random
import allure
import pytest
from pytest_demo_2.api_keyword.api_key import ApiKey
from pytest_demo_2.params.allParams import *
def pytest_collection_modifyitems(items):
"""
測試用例收集完成時,將收集到的item的name和nodeid的中文顯示在控制檯上
"""
for item in items:
item.name = item.name.encode("utf-8").decode("unicode_escape")
item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
# 專案級fix,整個專案只初始化一次
@pytest.fixture(scope='session')
def token_fix():
# 初始化工具類
ak = ApiKey()
with allure.step("傳送登入介面請求,並獲取token,整個專案只生成一次"):
# 請求介面
# url = 'http://39.98.138.157:5000/api/login'
url = URL + PORT + '/api/login'
# 請求引數
userInfo = {
'username': 'admin',
'password': '123456'
}
# post請求
res = ak.post(url=url, json=userInfo)
# 獲取token
token = ak.get_text(res.text, 'token')
# 驗證程式碼,驗證token只生成一次
token_random = random()
return ak, token, res, token_random
- 其中也包含了防止中文亂碼,加入了 pytest_collection_modifyitems(函式)。
設定好 conftest 後,就可以應用在邏輯層裡面了。
程式碼(shopingApi.py)如下:
import pytest
import allure
from pytest_demo_2.api_keyword.api_key import ApiKey
from pytest_demo_2.params.allParams import *
class ApiCase():
# 登入邏輯
def params_login(self, userdata):
# 動態獲取引數生成標題
allure.dynamic.title(userdata['title'])
# 初始化工具類
ak = ApiKey()
# 請求介面
url = URL + PORT + '/api/login'
# 請求引數
userInfo = {
'username': userdata['user']['username'],
'password': userdata['user']['password']
}
res = ak.post(url=url, json=userInfo)
with allure.step("介面返回資訊校驗及列印"):
print("/api/login登入介面請求響應資訊")
print(res.text)
# 獲取響應結果
msg = ak.get_text(res.text, 'msg')
print(msg)
# 斷言
assert msg == userdata['msg']
def params_getuserinfo(self, token_fix):
# 從fix中獲取預置的工具類和token,所有返回值都需要接收
ak, token, res, token_random01 = token_fix
with allure.step("傳送個人查詢介面請求"):
url = URL + PORT + '/api/getuserinfo'
headers = {
'token': token
}
res1 = ak.get(url=url, headers=headers)
with allure.step("介面返回資訊校驗及列印"):
print("/api/getuserinfo個人使用者查詢介面請求響應資訊")
print(res1.text)
# print("驗證的random值,測試用")
# print(token_random01)
name = ak.get_text(res1.text, 'nikename')
# 斷言
assert "風清揚" == name
return res1
def params_addcart(self, token_fix):
# 從fix中獲取預置的工具類和token
# 所有返回都要獲取,不然會報錯
ak, token, res, token_random01 = token_fix
with allure.step("呼叫getuserinfo介面獲取返回資訊"):
res1 = self.params_getuserinfo(token_fix)
with allure.step("傳送新增商品到購物車請求"):
# 新增商品到購物車,基於token、userid、openid、productid
url = URL + PORT + '/api/addcart'
hd = {
"token": token
}
data = {
"userid": ak.get_text(res1.text, 'userid'),
"openid": ak.get_text(res1.text, 'openid'),
"productid": 8888
}
# 傳送請求
res2 = ak.post(url=url, headers=hd, json=data)
with allure.step("介面返回資訊校驗及列印"):
print("/api/addcart新增商品到購物車請求響應資訊")
print(res2.text)
# print("驗證的random值,測試用")
# print(token_random01)
result = ak.get_text(res2.text, 'result')
assert 'success' == result
return res2
def params_createorder(self, token_fix):
ak, token, res, token_random01 = token_fix
with allure.step("呼叫addcart介面獲取返回資訊"):
res1 = self.params_addcart(token_fix)
with allure.step("傳送下單請求"):
url = URL + PORT + '/api/createorder'
# 從專案級fix中獲取token
hd = {
"token": token
}
# 從新增商品到購物車介面中獲取userid,openid,cartid
data = {
"userid": ak.get_text(res1.text, 'userid'),
"openid": ak.get_text(res1.text, 'openid'),
"productid": 8888,
"cartid": ak.get_text(res1.text, 'cartid')
}
res2 = ak.post(url=url, headers=hd, json=data)
with allure.step("介面返回資訊校驗及列印"):
print("/api/createorder下單請求響應資訊")
print(res2.text)
# print("驗證的random值,測試用")
# print(token_random01)
result = ak.get_text(res1.text, 'result')
assert 'success' == result
6、用例層
呼叫邏輯層進行用例管理和資料傳輸。
程式碼(test_Tree.py)如下:
import allure
import pytest
from pytest_demo_2.data_driver import yaml_driver
from pytest_demo_2.logic.shopingApi import ApiCase
@allure.epic("shopXo電商平臺介面-介面測試")
class TestTree():
# 初始化用例庫
actions1 = ApiCase()
@allure.feature("01.登陸")
@allure.story("02.一般場景")
@user8ize('userdata', yaml_driver.load_yaml('./data/user.yaml'))
def test_case01(self, userdata):
self.actions1.params_login(userdata)
@allure.feature("02.個人查詢")
@allure.story("01.典型場景")
@allure.title("個人查詢")
def test_case02(self, token_fix):
self.actions1.params_getuserinfo(token_fix)
@allure.feature("03.新增商品到購物車")
@allure.story("01.典型場景")
@allure.title("新增商品到購物車")
def test_case03(self, token_fix):
self.actions1.params_addcart(token_fix)
@allure.feature("04.下單")
@allure.story("01.典型場景")
@allure.title("下單")
def test_case04(self, token_fix):
self.actions1.params_createorder(token_fix)
7、執行
程式碼(main_run.py)如下:
import os
import pytest
def run():
pytest.main(['-v', './case/test_Tree.py',
'--alluredir', './result', '--clean-alluredir'])
os.system('allure serve result')
# os.system('allure generate ./result/ -o ./report_allure/ --clean')
if __name__ == '__main__':
run()
8、結果
更多內容可以訪問個人主頁學習《測試工程師 Python 工具開發實戰》書籍、《大話效能測試 JMeter 實戰》書籍