上一篇:http://www.reibang.com/p/d75f24e5de29
上一篇在一個py文件中厨钻,寫了一堆test_開頭的方法,所有數(shù)據(jù)和用例都在一個py文件中跌前,本篇嘗試讀取json文件的測試數(shù)據(jù),執(zhí)行用例。
技術(shù)準備
- httpbin:安裝信息見上一篇
- json:掌握json支持的數(shù)據(jù)格式和json的序列化操作
- pytest:pytest的參數(shù)化方式
-
requests:requests是如何發(fā)送http請求的
1翎卓、準備json格式的數(shù)據(jù)
- httpbin中的示例接口都是比較簡單的, 都沒業(yè)務(wù)邏輯的關(guān)聯(lián)啥的,按照requests.Request中要傳入的參數(shù)準備的數(shù)據(jù)摆寄。
- 可以把interface_info中的一個字典當作一個測試用例的數(shù)據(jù)失暴。
# data.json
{
"TestHttpMethods":
{
"interface_info": [
{
"interface_method": "get",
"method": "get",
"headers": null,
"url_data": null,
"data": {"test": "testdata"},
"params": null,
"auth": null,
"cookies": null,
"hooks": null,
"json": null,
"except": [200]
},
{
"interface_method": "post",
"method": "post",
"headers": null,
"url_data": null,
"data": {"test": "testdata"},
"params": null,
"auth": null,
"cookies": null,
"hooks": null,
"json": null,
"except": [200]
}
]
},
"TestAuth":
{
"interface_info": [
{
"interface_method": "basic-auth",
"method": "get",
"headers": null,
"url_data": ["testuser", "testpasswd"],
"data": null,
"params": null,
"auth": ["testuser", "testpasswd"],
"cookies": null,
"hooks": null,
"json": null,
"except": [200]
},
{
"interface_method": "bearer",
"method": "get",
"headers": {"Authorization": "justtestauth"},
"url_data": null,
"data": null,
"params": null,
"auth": null,
"cookies": null,
"hooks": null,
"json": null,
"except": [200]
}
]
}
}
2、讀取json文件中的數(shù)據(jù)
- get_case(): 用于讀取json文件中的數(shù)據(jù)微饥,并保存為字典格式逗扒,最后用yield返回一個生成器
- get_data(): 用于解析字典中的數(shù)據(jù),由于后續(xù)要采用pytest中的
@pytest.mark.parametrize
進行參數(shù)化欠橘,所以把每組數(shù)據(jù)都保持在一個元組中矩肩,元組存于列表中
# conftest.py
import sys
sys.path.append('.')
import json, codecs, os
print(os.getcwd())
def get_case():
with codecs.open('data.json', 'r', encoding='utf-8') as f:
f_dict = json.load(f)
for collection, cases in f_dict.items():
for case in cases['interface_info']:
yield {collection: case}
def get_data():
cases = get_case()
datas = []
for case_d in cases:
for collection, case in case_d.items():
url_method = case['interface_method']
method = case['method']
headers = case["headers"]
url_data = case['url_data'] # if case['url_data'] is None else tuple(case['url_data'])
data = case['data']
params = case['params']
auth = case['auth']
cookies = case['cookies']
hooks = case['hooks']
json = case['json']
except_data = case['except']
t = (url_method, method, headers, url_data, data, params, auth, cookies, hooks, json, except_data)
datas.append(t)
return datas
結(jié)果:
print(type(get_case()))
print(get_data())
<class 'generator'>
[('get', 'get', None, None, {'test': 'testdata'}, None, None, None, None, None, [200]),
('post', 'post', None, None, {'test': 'testdata'}, None, None, None, None, None, [200]),
('basic-auth', 'get', None, ['testuser', 'testpasswd'], None, None, ['testuser', 'testpasswd'], None, None, None, [200]),
('bearer', 'get', {'Authorization': 'justtestauth'}, None, None, None, None, None, None, None, [200])]
3、重寫一下requests的請求方法
- 由于在json文件中肃续,寫入了接口路徑的path部分和接口的請求方法黍檩,所以選擇requests.Request()方法發(fā)送請求叉袍,參照Request的源碼,將需要傳入的參數(shù)都在
__init__()
構(gòu)造方法中進行初始化 - 可以看到
__init__()
中用了非常經(jīng)典的三語表達式 - 因為url_data和auth在json中傳入的是列表刽酱,但是參數(shù)需要的實際格式是元組畦韭,所以當傳入的參數(shù)不是None時,需要轉(zhuǎn)換為元組
- 這個文件中肛跌,導(dǎo)入了一個config.py文件艺配,里面現(xiàn)在就一個參數(shù)
BASE_URL = 'http://192.168.68.128:8088/'
,主要用于存儲一些配置信息(如果后面發(fā)郵件或者連數(shù)據(jù)庫啥的,配置信息也可以寫在這里面) - url拼接:httpbin中衍慎,某些接口的url需要傳入與auth數(shù)據(jù)一致的信息转唉,所以采用urljoin進行拼接
# httpmethods.py
import sys
sys.path.append('.')
from urllib.parse import urljoin
import requests
from requests import Request, Session
import config
# print(config.BASE_URL)
class Http:
def __init__(self,
method=None, url=None, headers=None, files=None, data=None,
params=None, auth=None, cookies=None, hooks=None, json=None,
base_url=None, url_method=None, url_data=None):
# Default empty dicts for dict params.
data = [] if data is None else data
files = [] if files is None else files
headers = {} if headers is None else headers
params = {} if params is None else params
hooks = {} if hooks is None else hooks
url_data = () if url_data is None else tuple(url_data)
auth = None if auth is None else tuple(auth)
self.hooks = requests.hooks.default_hooks()
type(hooks)
for (k, v) in list(hooks.items()):
Request.register_hook(event=k, hook=v)
self.method = method
self.url = url
self.headers = headers
self.files = files
self.data = data
self.json = json
self.params = params
self.auth = auth
self.cookies = cookies
self.base_url = base_url
self.url_method = url_method
self.url_data = url_data
def method_new(self):
self.base_url = config.BASE_URL
s = Session()
url = urljoin(self.base_url, '/'.join((self.url_method,) + self.url_data))
print(url)
req = Request(method=self.method.upper(), url=url, headers=self.headers,
files=self.files, data=self.data, params=self.params, auth=self.auth,
cookies=self.cookies, json=self.json)
prepped = req.prepare()
# 如果需要設(shè)置代理,可以在s.send中添加并進行配置, 詳情查看send的源碼
resp = s.send(prepped)
return resp
4稳捆、采用pytest進行參數(shù)化
- 導(dǎo)入前面準備的文件赠法,采用
pytest.mark.parametrize
進行參數(shù)化 - 實例化重寫的請求發(fā)送方式,并傳入?yún)?shù)化數(shù)據(jù)
- 發(fā)送請求乔夯,接收結(jié)果并進行斷言
# test_run.py
import sys
sys.path.append('.')
import pytest
from httpmethods import Http
import conftest
@pytest.mark.parametrize("url_method, method, headers, url_data, data, params, auth, cookies, hooks, json, except_data",
conftest.get_data())
def test_case(url_method, method, headers, url_data, data, params, auth, cookies, hooks, json, except_data):
h = Http(method=method, url_method=url_method, headers=headers,
url_data=url_data, data=data, params=params, auth=auth,
cookies=cookies, hooks=hooks, json=json)
r = h.method_new()
assert r.status_code == except_data[0]
5砖织、運行pytest命令,執(zhí)行用例生成測試報告
pytest -q --tb=no --html=./report.html
總結(jié)
-
往前的一小步:學會了json文件的讀取末荐,雖然我覺得之前也是會的侧纯,但是在實際練習過程中發(fā)現(xiàn),對json支持的數(shù)據(jù)類型與python之間的轉(zhuǎn)換認識得仍然不夠深入:
- 不足之處:
1甲脏、從json文件可以看出眶熬,TestHttpMethods和TestAuth存在的目的是想要表示一個測試集,但是在用例實際執(zhí)行過程中沒有體現(xiàn)出來块请,對于pytest的使用不熟練娜氏,還不知道應(yīng)該如何結(jié)合起來;
2墩新、在命令行中使用pytest的命令執(zhí)行用例的方式不夠靈活贸弥;
3、郵件發(fā)送海渊、定時任務(wù)執(zhí)行等等绵疲,都是必要的。