为了实现真正意义上的接口自动化,一般使用yaml文件存储测试用例,代码调用里面的数据来发送请求
@Controller
@RequestMapping("/send")
public class Login {
@ResponseBody
@RequestMapping("/login")
public State login(String name,String password) {
if ("123".equals(name)&&"123".equals(password)) {
State state = new State();
state.setState(200);
return state;
}
State state = new State();
state.setState(101);
return state;
}
}
1、实现ymal文件的读取,并发送get请求
创建data.xml文件
- name: 登录接口
base_url: http://localhost:8080/send/login
request:
method: get
params: name=123&password=123
validate:
- equals: [ "status_code",200 ]
读取内容,并发送请求
import requests
import yaml
from selenium.webdriver.chrome import webdriver
from selenium.webdriver.common.by import By
import common.ymal
class TestCase:
def read_yaml(self,path):
# 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
with open(path, mode='r', encoding='utf-8') as f:
# 读取f文件流, 读取方式FullLoader,然后赋值给value
value = yaml.load(stream=f, Loader=yaml.FullLoader)
return value
def test_login(self):
requests_val=self.read_yaml("test_package/data.yml")
base_url=requests_val[0]["base_url"]
method=requests_val[0]["request"]["method"]
params=requests_val[0]["request"]["params"]
#发送请求
res=requests.session().request(method=method,url=base_url,params=params)
assert res.json()["state"]==200
method属性表示请求发送的类型
params:表示get请求传递的数据
json:表示json格式传递的数据
data:表示以form表单格式传递的数据
2、发送post请求:设置请求头的数据类型,使用对应方式传参
class TestCase:
def read_yaml(self,path):
# 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
with open(path, mode='r', encoding='utf-8') as f:
# 读取f文件流, 读取方式FullLoader,然后赋值给value
value = yaml.load(stream=f, Loader=yaml.FullLoader)
return value
def test_login(self):
requests_val=self.read_yaml("test_package/data.yml")
base_url=requests_val[0]["base_url"]
method=requests_val[0]["request"]["method"]
data=requests_val[0]["request"]["data"]
hearder=requests_val[0]["request"]["hearders"]
res=requests.session().request(method=method,url=base_url,data=data,headers=hearder)
assert res.json()["state"]==200
3、实现文件上传
@Controller
@RequestMapping("/send")
public class upload {
@PostMapping("/upload")
public void Upload(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws IOException {
String name = file.getOriginalFilename();
response.setStatus(200);
File newfile = new File("C:\\Users\\30283\\Desktop\\novel\\photo\\" ,name);
file.transferTo(newfile);
}
}
一般需要设置Content-Type: multipart/form-data,但是文件上传时会自动添加,如果自己添加了可能会报错
以files关键字来传递文件数据
- name: 文件上传接口
base_url: http://localhost:8080/send/upload
request:
method: post
files:
C:\\Users\\30283\Pictures\\picture\\七七.png
validate:
- equals: [ "status_code",200 ]
class TestCase:
def read_yaml(self,path):
# 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
with open(path, mode='r', encoding='utf-8') as f:
# 读取f文件流, 读取方式FullLoader,然后赋值给value
value = yaml.load(stream=f, Loader=yaml.FullLoader)
return value
def test_login(self):
requests_val=self.read_yaml("test_package/data.yml")
base_url=requests_val[0]["base_url"]
method=requests_val[0]["request"]["method"]
file_path=requests_val[0]["request"]["files"]
file_data = {'file': ("七七.png", open(file_path, 'rb'), 'image/png')}
#以二进制流的形式打开文件
requests.session().request(method=method,url=base_url,files=file_data)
把目标文件以open打开,然后存储到变量,并且使用files参数指明请求的参数名称、上传文件的类型、以及上传文件的路径。
4、实现接口关联
比如淘宝:只有用户登录之后,才可以加入商品到购物车,这时就要设置接口关联,使用新的ymal文件存储关联数据
@Controller
@RequestMapping("/set")
public class SetCookie {
@RequestMapping("/setcookie")
@ResponseBody
public String set(HttpServletRequest request,HttpServletResponse response) {
return "{\"token\":\""+UUID.randomUUID()+"\"}";
}
}
封装ymal文件操作方法
# 文件说明:yaml文件的读取与写入
import yaml
#读取ymal文件的信息
def read_yaml(path):
# 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
with open(path, mode='r', encoding='utf-8') as f:
# 读取f文件流, 读取方式FullLoader,然后赋值给value
value = yaml.load(stream=f, Loader=yaml.FullLoader)
return value
#给yaml文件追加信息
#为什么追加:让这个文件可以存储多个关联信息
def write_yaml(path, data):
with open(path, mode='a', encoding='utf-8') as f:
# 写入的数据从data传入
yaml.dump(data=data, stream=f,allow_unicode=True) #allow_unicode=True支持中文
#将这个clear_yaml配置到conftest中,这样可以清空之前的关联信息
# 清空yaml文件内容
def clear_yaml(path):
# 打开yaml文件
with open(path, mode='w', encoding='utf-8') as f:
# 清空yaml文件
f.truncate()
#读取文件的某一个数据
def read_value(path,key):
# 打开yaml文件,读取方式为只读r,编码格式utf-8, 然后重命名为 f
with open(path, mode='r', encoding='utf-8') as f:
# 读取f文件流, 读取方式FullLoader,然后赋值给value
value = yaml.load(stream=f, Loader=yaml.FullLoader)
return value[key]
在conftest.py文件中,让clear_yaml()方法设置自动使用
@pytest.fixture(scope="session",autouse=True)
def clear():
common.ymal.clear_yaml("C:/python学习/python自动化测试/temp.ymal")
class TestCase:
def test_login(self):
requests_val= common.ymal.read_yaml("test_package/data.yml")
base_url=requests_val[0]["base_url"]
method=requests_val[0]["request"]["method"]
res=requests.session().request(url=base_url,method=method)
#将返回的中间数据存储到ymal文件中
common.ymal.write_yaml("temp.ymal", res.json())
def test_check(self):
print(common.ymal.read_value("temp.ymal","token"))
token保留在中间文件中,并且每次保留的都是最新值
5、可以使用extract关键字提取接口的返回结果
@Controller
@RequestMapping("/set")
public class SetCookie {
@RequestMapping("/setcookie")
@ResponseBody
public String set(HttpServletRequest request,HttpServletResponse response) {
HttpSession session=request.getSession(true);
UUID val=UUID.randomUUID();
session.setAttribute("token",val);
return "{\"token\":\""+val+"\"}";
}
}
@Controller
@RequestMapping("/get")
public class GetCookie {
@ResponseBody
@RequestMapping("/getcookie")
public String get(String token,HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("token") != null)
return "ok";
return "error";
}
}
1、yaml文件新建extract参数,里面填写需要从当前接口的出参里提取的字段。提取字段有两种方法,第一种是正则提取,第二种是json数据提取
2、写文件
判断yaml文件的格式,是否有extract参数,接着判断提取的字段是正则提取还是json提取。对请求进行封装,将字段获取之后写入到extract.yaml文件即可。
安装插件:pip.exe install jsonpath -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com SomePackage
通过JSONPath表达式,使得从多层嵌套JSON数据中提取数据变得非常简单。
#规范YAML测试用例 #1、要求包含一级关键字 name,request,validate #2、在request一级关键字下,包含method和url字段 #如果是get请求,通过params关键字传参 # 如果是post请求 传json格式,使用json关键字传参 #如果是post请求 传表单格式,使用data关键字传参 #如果是files请求,使用file传参
1、在根目录下,创建extract.yaml文件
2、创建py文件,封装方法,实现对ymal文件格式的验证和方法的请求,并实现extract提取关键字
#!/usr/bin/python
# -*- coding: UTF-8 -*
import json
import re
import jsonpath
import requests
import common.ymal
from debug_talk import DebugTalk
class Unifiedrequest:
def __init__(self,two,obj):
#self.url = common.ymal.read_value("base", two) #这里,从文件获取base,来获取url前面的公共部分
self.url="" #这里是因为我的url就是全部路径
#获取一个对象
self.obj=obj
#传入的是Debug_Talk文件的类对象,可以调用这个类里面的方法
#数据替换,data为获取到${}中的数据
#数据类型可能(string,int,float,list,dict),需要先数据转换
def read_token(self,data):
if data:
#保存原数据类型
data_type=type(data)
#数据类型转换
if isinstance(data,dict) or isinstance(data,list):
str_data=json.dumps(data)
else:
str_data=str(data)
for ce in range(1,str_data.count("${")+1):
if "${" in str_data and "}" in str_data:
index=str_data.index("${")
indexend=str_data.index("}",index)
old_value=str_data[index:indexend+1]
#获取对象属性
fun_name=old_value[2:old_value.index("(")]
fun_values=old_value[old_value.index("(")+1:old_value.index(")")]
fun_values_new=fun_values.split(",")
#*fun_values_new 解包 列表
if fun_values_new != " ":
#getattr() 函数用于返回一个对象属性值。
#self.obj为对象名称
new_value=getattr(self.obj,fun_name)(*fun_values_new)
str_data=str_data.replace(old_value,str(new_value))
else:
new_value = getattr(self.obj,fun_name)()
str_data = str_data.replace(old_value, str(new_value))
if isinstance(data,dict) or isinstance(data,list):
data=json.loads(str_data)
else:
data=data_type(str_data)
print(data)
return data
#yaml文件封装
def send_yaml(self,arges_name):
yaml_key = arges_name.keys()
if "name" in yaml_key and "request" in yaml_key:
yaml_request=arges_name["request"]
request_key = yaml_request.keys()
if "method" in request_key and "url" in request_key:
method=yaml_request.pop("method")
url=yaml_request.pop("url")
res=self.send_request(method,url,**arges_name["request"])
res_text=res.text
#状态码
res_status=res.status_code
res_json=""
try:
res_json = res.json()
except Exception as e:
print("jsonpath数据类型必须是json格式")
if "extract" in yaml_key:
for key,values in arges_name["extract"].items():
if "(.*?)" in values or "(.+?)" in values:
zz_value=re.search(values,res_text)
if zz_value:
common.ymal.write_yaml("extract.yaml",{key:zz_value.group(1)})
else:
jp_values=jsonpath.jsonpath(res_json,values)
if jp_values:
common.ymal.write_yaml("extract.yaml",{key:jp_values[0]})
else:
print("request中缺少关键字段:method,url")
else:
print("yaml文件第一阶段缺少关键字段:name,request")
session=requests.session()
def send_request(self,method,url,**kwargs):
url = self.url + self.read_token(url)
for key,value in kwargs.items():
if key in ["params","json","data","headers"]:
kwargs[key]=self.read_token(value)
elif key == "files":
for file_key,file_value in value.items():
value[file_key] = open(file_value,"rb")
res=Unifiedrequest.session.request(method,url,**kwargs)
print(res.text)
return res
3、请求yaml文件如下,使用extract关键字,提取某一个值
- name: 设置cookie接口
request:
method: post
url: http://localhost:8080/set/setcookie
validate:
- equals: ["status_code",200 ]
extract:
token: '"token":"(.*?)"'
# 或者 $.token
- name: 获取关联的token值
request:
method: post
url: http://localhost:8080/get/getcookie
data:
token: ${read_value(token)}
validate:
- equals: [ "status_code",200 ]
4、设置conftest.py的夹具方法,清除extrcat.yaml保留的token值
import pytest
from selenium import webdriver
import common.ymal
@pytest.fixture(scope="session",autouse=True)
def clear():
common.ymal.clear_yaml("extract.yaml")
5、运行
import pytest
import requests
import yaml
from selenium.webdriver.chrome import webdriver
from selenium.webdriver.common.by import By
import common.ymal
import debug_talk
from common.package import Unifiedrequest
class TestCase:
# debug_talk.DebugTalk()是在更目录创建的动态参数类方法
@pytest.mark.parametrize("args_name", common.ymal.read_yaml("test_package/data.yml"))
def test_post_tags(self, args_name):
Unifiedrequest("base_url", debug_talk.DebugTalk()).send_yaml(args_name)
6、热加载
热加载:就是在代码运行的过程中动态的调用python中的方法达到获得动态参数的目的
在工程目录下,创建debug_talk文件,在里面写一个获取随机数的方法
#!/usr/bin/python
# -*- coding: UTF-8 -*
import random
import time
import common.ymal
from common import ymal
class DebugTalk:
# 获得随机数
def get_randon_number(self, min, max):
return random.randint(int(min), int(max))
def get_time(self, key):
return int(time.time())
# 读取鉴权码yaml文件--token.yaml
def read_value(self,key):
return common.ymal.read_value("extract.yaml",key)
封装文件内修改一部分:
if "${" in str_data and "}" in str_data:
index=str_data.index("${")
indexend=str_data.index("}",index)
old_value=str_data[index:indexend+1]
#获取对象属性
fun_name=old_value[2:old_value.index("(")]
fun_values=old_value[old_value.index("(")+1:old_value.index(")")]
fun_values_new=fun_values.split(",")
#*fun_values_new 解包 列表
if fun_values_new != " ":
#getattr() 函数用于返回一个对象属性值。
new_value=getattr(self.obj,fun_name)(*fun_values_new)
str_data=str_data.replace(old_value,str(new_value))
else:
new_value = getattr(self.obj,fun_name)()
str_data = str_data.replace(old_value, str(new_value))
然后,可以在ymal文件中动态获取这个参数
- name: 设置cookie接口
request:
method: post
url: http://localhost:8080/set/setcookie
validate:
- equals: ["status_code",200 ]
extract:
token: '"token":"(.*?)"'
# 或者 $.token
- name: 获取关联的token值
request:
method: post
url: http://localhost:8080/get/getcookie
data:
token: ${read_value(token)}
validate:
- equals: [ "status_code",200 ]
- name: 获取随机数字
request:
method: post
url: http://localhost:8080/send/login
data:
username=${get_randon_number(100,999)}&password=${get_randon_number(1000,9999)}
validate:
- equals: [ "status_code",200 ]
参考
pytest框架之热加载_python 热加载_Beck_k的博客-CSDN博客