Requests库
Requests库 是 Python编写的,基于urllib 的 HTTP库,使用方便。
安装:pip install requests
pip 中查验:pip list
设置http请求语法
resp = requests.请求方法(url='URL地址', params={k:v}, headers={k:v}, data={k:v}, json={k:v}, cookies='cookie数据'(如:令牌)) 请求方法: get请求 - get() post请求 - post() put请求 - put() delete请求 - delete() url: 待请求的url - string类型 params:查询参数 - 字典 headers:请求头 - 字典 data:表单格式的 请求体 - 字典 json:json格式的 请求体 - 字典 cookies:cookie数据 - string类型 resp:响应结果
get传参
#方式一发送 get 请求,指定 url,获取 响应结果 import requests ID="wwa87beca40d2d6e23" SECRET="Gt90K4qyPDltlzAByr5FTXINkRjx0iybK8Z3WUz-3fE" url = f"https://ayapi.weixin.qq.com/cgi-bin/gettoken" param = { "corpid":ID, "corpsecret" : SECRET } r = requests.request("get", url,params=param) r.encoding ="utf-8" print(r.text) print(r.request.url)
#方式二 import requests ID="wwa87beca40d2d6e23" SECRET="Gt90K4qyPDltlzAByr5FTXINkRjx0iybK8Z3WUz-3fE" url = f"https://ayapi.weixin.qq.com/cgi-bin/gettoken?corpid={ID}&corpsecret={SECRET}" r = requests.request("get", url) r.encoding ="utf-8" print(r.text)#返回文本型#print(r.json())返回json字典型 print(r.request.url)
post传参
import requests ID="wwa87beca40d2d6e23" SECRET="Gt90K4qyPDltlzAByr5FTXINkRjx0iybK8Z3WUz-3fE" url = f"https://ayapi.weixin.qq.com/cgi-bin/gettoken" param = { "corpid":ID, "corpsecret" : SECRET } r = requests.request("get", url,params=param) r.encoding ="utf-8" json_res=r.json() print(r.request.url) url = "https://qyapi.weixin.qq.com/cgi-bin/department/create" param = { "access_token":json_res["access_token"] } data = { "name" :"广州研发中心", "parentid":1 } #json格式 r = requests.request("post" ,url,params=param,json=data) print(r.json()) print(r.headers) #表单格式 r = requests.request("post" ,url,params=param,json=data,data=数据)
带请求头,带 json数据的post请求
import requests ID="wwa87beca40d2d6e23" SECRET="Gt90K4qyPDltlzAByr5FTXINkRjx0iybK8Z3WUz-3fE" url = f"https://ayapi.weixin.qq.com/cgi-bin/gettoken" param = { "corpid":ID, "corpsecret" : SECRET } r = requests.request("get", url,params=param) r.encoding ="utf-8" json_res=r.json() print(r.request.url) url = "https://qyapi.weixin.qq.com/cgi-bin/department/create" param = { "access_token":json_res["access_token"] } data = { "name" :"广州研发中心", "parentid":1 } headers ={ "content-type" : "application/json", "aa": "vv" } r = requests.request("post",url,params=param,json=data,headers=headers) print(r.json()) print(r.request.headers)
post请求文件上传,用cookie校验
import requests url= "https://nmtp.youdao.com/api/organize/1735997/image/upload" headers = { "Cookie":"OUTFPOX.SEARCY.USE3._10-46.8701810.110.96.157;OJITFOX SEARCB_.USER.10.4C0=-2083621146.258.8;ga=BA1.2.1641428806.1650124294" } file= { "file":open("文件路径") } r = requests.request("post", url,headers=headers,files=file) print(r.text)
request请求参数
def http_request(self, method, url, **kwargs): """ :param method: 接口请求方式,与Restful一致,入参可以为:get,put,delete,post等 :param url: 接口请求的url地址 :param kwargs: 请求参数, 包括params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json等 method----接口请求方式,值分别对应以上四个方法名称,例如:请求方式为get,则method=‘get’ url----接口的url地址 params----get请求的参数 data----post和put请求除json格式以外的参数 headers----接口请求头 cookies----请求头里面的cookie信息 files----用于文件上传,传参文件流 allow_redirects----是否允许重定向 json ----post请求的json格式传参 :return:返回响应体 """
request查看模块返回信息
使用headers来查看响应信息
使用text来查看正文
使用encording来查看编码
使用status_code来查看响应码
使用content来查看二进制形式,如返回图片的二进制信息
使用elapsed.total_seconds()来查看接口的响应时间
查看cookies
如果接口文档中没有明确的规定说,请求是json格式的,那么请求都是放在字典中的
cookies,向服务器表明请求是同一个账号发的
一般来说,登录成功后会存在cookies
cookies的获取方式:直接用return返回值.cookies即可;
cookies其实是一种类字典的形式,可以用字典取值的方式进行取值
import requests url = "https: //vd3.bdstatic.com视频音乐图片等路径" r = requests.request("get", url) print(r.content)#获取二进制形式 with open("1.mp4","wb") as f f.write(r.content)#保存为视频 sss
pytest+requests+yaml例
demo.yaml#yaml数据文件
- name:广州研发中心 parentid: 1 expect: 0#断言 - name: "" parentid:1 expect: 40058#断言 错误码匹配 - name:广州研发中心 parentid: expect: 60004#断言错误码匹配
main.py#主运行入口文件
import pytest if __name__== " __main__" pytest.mian()
utils.py#读取yaml文件工具是一个包
import yaml def read_yaml(path): with open(path,"r",encoding="utf-8") as f: data = yaml.safe_load(f) return data
test_demo.py#测试文件
import pytest import requests from utils import read_yaml class TestDem(): def setup_class(self):#获取保存access_token ID = "wwa87beca40d2d6e23" SECRET = "Gt90K4qyPDltlzAByr5FTXINkRjx0iybK8Z3WUz-3fE" url = f"https://qyapi.weixin.qq.com/cgi-bin/qettoken" param = { "corpid" : ID, "corpsecret":SECRET } r = requests.request("get", url, params=param) self.token = r.json()["access_token"] @pytest.mark.parametrize("case",read yaml("demo.yaml"))#数据驱动case变量接受参数 def test_01(self, case):#测试用例 url = "https://ayapi.weixin.qq.com/cgi-bin/department/create" param = { "access_token":self.token } data= { "name" : case["name"], "parentid" : case["parentid"] } r = requests.request("post",url,params=param,json=data) assert r.json["errcode"]== case["expect"]#断言错误码匹配检查
框架封装
框架介绍
python接口自动化采用的框架是:python+request+pytest+excel/yaml+allure
创建不同Python包进行分层封装
config:存放config.ini配置文件。
org_business:需要进行测试的接口及其参数。
org_data:数据层这里将测试数据放在excel中,通过read_data方法读取。
org_logs:生成的日志文件。
org_report:存放生成的测试报告。
org_testcase:测试层存放测试用例。
conftest.py:存放了一些通用方法(读Excel数据,比较json串等)。
pytest.ini:主配置文件,可以改变pytest的默认行为。
requirements.txt:项目依赖包。
run_org_api:主程序入口,执行测试用例
还有
protocl:协议层一般存放封装请求协议,不同协议分类存放在此层
apis:存放所有的api设计框架
utils:工具层一般存放日志,文件读取,数据库操作工具
data/demo.yaml请求参数
- name: "" parentid:1 expect: 40058#断言 错误码匹配 - name:广州研发中心 parentid: expect: 60004#断言错误码匹配
apis/dept.py
import random from apis.qywx_api import weWork class DeptHent(weWork) : def create(self): url = "https://qyapi.weixin.qq.com/cgi-bin/department/create" data = { "name": f"test_{random.randint(0,999999)}", "parentid":1 } r = self.send("post", url,json=data) return r
apis/qywx_api.py
from protocl.http_protocol import Httpclien class WeWork(): def __init__(self): self.__httpClien = Httpclien() self.__token = self.__get_token() def __get_token(self): ID = "wwa87beca40d2d6e23" SECRET = "Gt90K4qyPDltlzAByr5FTXINkRjxOiybK8Z3WUz-3fE" url = f"https://qyapi.weixin.qq.com/cqi-bin/gettoken" param = { "corpid":ID, "corpsecret": SECRET } r = self.-_httpClien.send("get",url,params=param) return r.json()["access_token"] def wechat_send(self,method,url,**kwargs): if kwargs.get("params"): kwargs["params"]["access_token"] = self.__token else: kwargs["params"]= {"access_token":self.__token} res = self.__httpClien.send(method,url,**kwargs) return res
protocl/http_protocl.py
import requests class Httpclien: def send(self, method,url,**kwargs) : res = requests.request(method, url,**kwargs) return res
testcase/test_demo.py
import pytest import requests from apis.dept import DeptNent class TestDemo: def setup_class(self): self.dept = DeptMent( def test_01(self): self.dept.create()
优化抽出域名id,作成配置文件
config/env.yaml指定在哪个环境下
qywx_host: default: test test: https://qyapi.weixin.qq.com pro: https: //xxx.weixin.qq.com
config/secrets.yaml存放id
corpid : test: wwa87beca40d2d6e23 pro:XXXX corpsecret : test: Gt90K4qyPDltlzAByr5FTXIHkRjx0iybK8Z3WUz-3fE pro: x×××
utils/yaml_utils.py写读配置文件方法
import os.path import yaml root = os.path.dirname(os.path.dirname(__file__)) def read_yaml(path): with open(path,"r", encoding="utf-8") as f: data = yaml.safe_load(f) return data def read_config(filename): path = os.path.join(root,"config",filename) with open(path,"r", encoding="utf-8") as f: data = yaml.safe_load(f) return data
apis/qywx_api.py
from protocl.http_protocol import Httpclien class WeWork(): def __init__(self): self.__httpClien = Httpclien() #获取不同环境域名 env=read config("env. yaml") default = env[ " aywx_host"]["default"] self.base_url =env[ " qywx_host"][default] #获取不同环境的id secrets = read_config("secrets.yaml") self.__corpid = secrets[ "corpid"][default] self.__corpsecret = secrets[ "corpsecret"][default] self.token = self.__get_token() def __get_token(self): url = "/cqi-bin/gettoken" param = { "corpid":self.__corpid, "corpsecret": self.__corpsecret } r = self.__httpClien.send("get",self.base_url,params=param) return r.json()["access_token"] def wechat_send(self,method,url,**kwargs): if kwargs.get("params"): kwargs["params"]["access_token"] = self.__token else: kwargs["params"]= {"access_token":self.__token} res = self.__httpClien.send(method,self.base_url,**kwargs) return res
apis/dept.py
import random from apis.qywx_api import weWork class DeptHent(weWork) : def create(self): url = "/cgi-bin/department/create" data = { "name": f"test_{random.randint(0,999999)}", "parentid":1 } r = self.send("post", url,json=data) return r
优化请求参数,参数替换
data/demo.yaml请求参数
- 参数name为空: name: "" parentid:1 expect: 40058#断言错误码匹配 - 参数parentid为空: name:广州研发中心 parentid: expect: 60004#断言错误码匹配
utils/yaml_utils.py写读配置文件方法
import os.path import yaml root = os.path.dirname(os.path.dirname(__file__)) def read_yaml(filename): path = os.path.join(root,"data",filename) with open(path,"r", encoding="utf-8") as f: data = yaml.safe_load(f) return data def read_config(filename): path = os.path.join(root,"config",filename) with open(path,"r", encoding="utf-8") as f: data = yaml.safe_load(f) return data
protocl/http_protocl.py
import requests class Httpclien: def send(self, method,url,json_data=None,**kwargs): if json_data is not None: for key,value in json_data.items(): if "headers" in kwargs.keys(): self.replace_val(kwargs["headers"],f"$..{key}", value) if "params" in kwargs.keys(): self.replace_val(kwargs["params"],f"$..{key}", value) if "json" in kwargs.keys(): self.replace_val(kwargs["json"],f"$..{key}", value) if "data" in kwargs.keys(): self.replace_val(kwargs["data"],f"$..{key}", value) res = requests.request(method, url,**kwargs) return res def replace_val(self, data, jsonpath_expr,value) : p = parse(jsonpath_expr) return p.update(data,value)
apis/dept.py
import random from apis.qywx_api import weWork class DeptHent(weWork) : def create(self,json_data=None): url = "/cgi-bin/department/create" data = { "name": f"test_{random.randint(0,999999)}", "parentid":1 } r = self.send("post", url,json=data,json_data=json_data) return r
apis/qywx_api.py
from protocl.http_protocol import Httpclien class WeWork(): def __init__(self): self.__httpClien = Httpclien() #获取不同环境域名 env=read config("env. yaml") default = env[ " aywx_host"]["default"] self.base_url =env[ " qywx_host"][default] #获取不同环境的id secrets = read_config("secrets.yaml") self.__corpid = secrets[ "corpid"][default] self.__corpsecret = secrets[ "corpsecret"][default] self.token = self.__get_token() def __get_token(self): url = "/cqi-bin/gettoken" param = { "corpid":self.__corpid, "corpsecret": self.__corpsecret } r = self.__httpClien.send("get",self.base_url,params=param) return r.json()["access_token"] def wechat_send(self,method,url,json_data=None,**kwargs): if kwargs.get("params"): kwargs["params"]["access_token"] = self.__token else: kwargs["params"]= {"access_token":self.__token} res = self.__httpClien.send(method,self.base_url,json_data=json_data,**kwargs) return res
testcase/test_demo.py
import pytest import requests from apis.dept import DeptNent class TestDemo: def setup_class(self): self.dept = DeptMent( def test_01(self): self.dept.create() assert r.json()[ "errcode" ] == 0 @pytest.mark.parametrize("case",read_yaml("demo.yaml")) def test_02(self, case): for key,value in case.items(): r=self.dept.create(value) assert r.json()[ "errcode" ] == value[ "expect" ]
日志封装
utlis/logger_util.py
import logging import os root = os.path.dirname(os.path.dirname(__file__)) #绑定logging的句柄 logger = logging.getLogger(__name__) #拼接logs的文件夹 file_path = os.sep.join([root, "logs"]) #如果文件夹不存在就创建 if not os.path.exists(file_path): os.mkdir(file_path) #拼接log文件夹的路径和句柄 fileHandler = logging.FileHandler(filename=file_path + "/apitest.log",encoding="utf-8") #日志的格式定义 formatter = logging.Formatter(' [%(asctime)s] %(filenane)s -%(funcane)s line :%(lineno)d [%(levelname)s]: %(message)s') #文件句柄绑定格式 fileHandler.setFormatter(formatter) #控制台句柄的定义 streamHandler = logging.streamHandler() #控制台句柄睇定格式 streamHandler.setFormatter(formatter) #设置生效 logger.addHandler(fileHandler) logger.addHandler(streamHandler) #设置日志的级别 logger.setLevel(logging.INFO)
需要哪里打印日志就在那里添加日志工具
protocl/http_protocl.py
import requests class Httpclien: def send(self, method,url,json_data=None,**kwargs): if json_data is not None: for key,value in json_data.items(): if "headers" in kwargs.keys(): self.replace_val(kwargs["headers"],f"$..{key}", value) if "params" in kwargs.keys(): self.replace_val(kwargs["params"],f"$..{key}", value) if "json" in kwargs.keys(): self.replace_val(kwargs["json"],f"$..{key}", value) if "data" in kwargs.keys(): self.replace_val(kwargs["data"],f"$..{key}", value) #添加日志工具 logger.info(f"请求地址:{url}") logger.info(f"请求参数:\n{json.dumps(kwargs,indent=2,ensure_ascii=False)}") res = requests.request(method, url,**kwargs) logger.info(f"响应结果:\n{json.dumps(res.json(),indent=2,ensure_ascii=False)}") return res def replace_val(self, data, jsonpath_expr,value) : p = parse(jsonpath_expr) return p.update(data,value)
还有数据库,方法等一些小功能封装