自动化测试
测试环境搭建httpbin.org开源接口网站
http://www.httpbin.org/这个网站是开放的用来做测试用网站,外网注意看脸进
笔者本来想搭一个docker+wsl2+git的本地环境,但碍于win11某些问题失败了,后面去b站找了一个Jupyter搭建的本地项目
Jupyter Notebook介绍、安装及使用教程 - 知乎 (zhihu.com)
网络爬虫_使用Web测试工具httpbin_哔哩哔哩_bilibili
jupyter notebook #jupyter安装完成后启用jupyter,默认8888端口,
#http://localhost:8888/tree
#进入网页后新建一个terminal的终端
python -m httpbin.core#安装httpbin.org web服务,默认5000端口
#网址 http://localhost:5000/
感谢大佬的开源接口
https://blog.csdn.net/chao/article/details/78573737
api文档
https://api.apiopen.top/api.html#1353cada190b48a095fe10c40d5dd529
自动化测试行情
主流接口测试框架
postman+newman+git+jenkins
jmeter+ant+git+jenkins
主流接口自动化测试框架:
python+requests+pytest+allure+jenkins
request库简介
python第三方库,用于接口自动化测试
requests第三方库安装方式
#项目控制台
pip install requests
#有些情况下这个命令完毕后py脚本导入requests库仍然是失败的,此时我们使用的是Pycharm的话可以通过编辑器去进行安装
#file->settings->找到当前项目:->python interpreter->右侧选中加号->搜索requests->选中install package,安装成功会进行提示
https://blog.csdn.net/weixin_37861326/article/details/80347960
常用方法
request.get() #传入参数,url接口地址、params传递参数
requests.post()
#url接口地址 data用于传参 json用于传参
#data和json传参区别:作用是服务器要求传入的请求头Content-Type区分
#Content-Type作用是服务器要求传入的保温内容的类型,用于区分传参类型
#请求:请求方式,请求路径,请求头、请求正文
#postman四种传递参数对应的Content-type值如下:
#form-data:content=Type:multipart/form-data"boundary=<calculated when request is sent> 文件上传
#x-www-form-urlencoded:content-Type:application/x-www-form-urlencoded 表单提交
#raw:
#text:Content-Type:text/plain
#javascript:Content-Type:application/javascript
#json:Content-Type:text/html
#html:content-Type:application/xml
#xml:Content-Type:application/binary
#binary:content-Type:application/binary
#data和json传参与content-type的关系:
#1、data传参:报文类型是dict字典类型,默认Content-type:aaplication/x-www-form-urlencoded
# data传参:报文是str类型,默认Content-type:text/plain
#2、json传参:报文是dict也可以是str类型,默认Content-Type:application/json
#看接口报文头content-type判断是什么传参
#data:可以传简直对的dict(非嵌套的字典dict),可以传递str格式
#json:可以传任何形式的dict(包括嵌套的dict,字典嵌套字典,字典嵌套字典在嵌套列表i)
#{"tag":{"tag1":[44,"454","io"]}}
requests.put()
requests.delete()
requests.request(Method='POST/GET')#支持发送所有请求,这个才是用的最多,指定发送方式,requests.get()、requests.POST()本质还是调用request方法传递参数
requests.request()
res = requests.request(method=method,url=url,data=data,json=json,headers=headers,cookies=cookies,files=files,params=params)
#method 请求方式
#url 请求路径
#params get传参
#json post传参
#data post传参
#headers 请求头
#cookies 请求cookies
#files 文件上传
requests,模块返回的response对象详解
请求四要数:请求方式、请求路径、请求头、请求正文
当请求失败,又确保四要素都存在且没有问题,那么轻轻检查请求头header,大概率是header不正确
res.json() #字典格式数据,json格式
res.text #返回字符串格式数据
res.content#返回bytes字节类型
res.status_code#返回状态码
res.cookies #cookies数据
res.reason#返回状态信息
res.request.xxx#返回请求数据。请求头
res.headers
res
import requests
import json
#requesets 实际使用
class TestRequest:
#基本使用方式
def test_get_response(self):
url = "http://127.0.0.1:5000/get"
data = {
"grant_type": "123",
"appid": "123",
"sercet": "5555"
}
res = requests.get(url=url, data=data)
print(res.text)
#打印response返回的能返回的所有信息,args,header、json、url等
#简单应用
def test_get_uuid(self):
url = "http://127.0.0.1:5000/get"
url2 = "http://www.httpbin.org/get"
url3 = "https://www.maoyan.com/board/4"
url_post = "http://127.0.0.1:5000/post"
str = "4545s4s45ss"
data = {
"tag": {"id": 134, "name": "heilongjiang"}
# 这种不是存粹键值对
}
headers = {
"Accept": "",
"Accept-Encoding": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Cache-control": "max-age=0",
"Connection": "keep-alive",
"Cookie": "BDUSS=VY0Nurytuyjhgh;iogfsgdhfjghcx"
}
# res = requests.get(url=url,params=str,headers=headers)
# #请求头有多个信息使用headers,单个信息用header
# # res9 = json.loads(data)
# print(res.headers)#拿什么信息就使用什么参数
# 模拟不同页数返回内容
# for i in range(10):
# params ={
# "offset":i*10
# }
# res = requests.get(url=url,params=params)
# print(res.text)
# print(res.url)#直接返回url
# 模拟上传文件
file = {
"file": open("C:\\ProgramEdit\\pythonProject\\202112245_03\\pytest\\users_dev.json", "r")
}
# post
res = requests.post(url=url_post, data=data, headers=headers)
print(res.text)
# post返回内容
# 返回参数arg \data \file \form请求参数
# res = requests.poet(url=url_post,json=data,headers=headers)
# 这种content-type格式是json的
# post请求方式不同,content-type格式不同
# res.content.decode=res.text
# 这两个作用是一样的,都是为了获得数据
# print("er jin zhi ",res.content.decode("utf-8"),type(res.content),type(res.content.decode()))
# <class 'bytes'> <class 'str'>
# print(res.text,type(res.text))
# 进text模块去看就是调用了content模块然后在进行解码
# try:
#response返回可调用参数简介
def test_response(self):
url = "http://127.0.0.1:5000/post"
url2 = "http://www.httpbin.org/get"
headers = {
"Accept": "",
"Accept-Encoding": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Cache-control": "max-age=0",
"Connection": "keep-alive",
"Cookie": "BDUSS=VY0NFJ3eWJqeWZ"
}
data = {
"hj": {"5545": 444, "jkl": [45, 6, 3, 88]},
"json": {"jsontest": 44}
}
params = {
"page": 1,
"limit": 20
}
res = requests.post(url=url, files=file, data=data, json=data, headers=headers, params=params)
print(res.text)
print(res.json().get("headers")) # 这个是本地请求的header,
print(res.json().get("form"))print()
print(res.json().get("Cookies"))print()
print(res.headers.get('Date'))
print(res.headers) # 这个是response返回后的header
print(res.cookies) # 请求request发出后,response返回接受后的cookies
print(res.history) # response返回请求后,检查是否被重定向,显示内容为重定向的某个网站
print(res.raw) # 原始响应,表示流式操作,使用raw需要stream=True,二进制内容,http的Socket
#下载文件尝试
def test_load(self):
url="http://127.0.0.1:5000/image/png"
res = requests.get(url =url,params="555")
#加载文件前确定文件格式
type = res.headers.get("Content-Type").spilt("/")[-1]#content-type会返回文件的格式,我们这里是图片,[-1]表示从后面拿
filename="1"+type
with open(filename,"wb") as f:
f.write(res.content)
#session使用
def test_session(self):
session = requests.Session() # 创建Sesisson式会声明两个Adapter、绑定HTTPAdapter方法
session.get() # requests.get()一致但是会保存一些操作,response.headers.get("set-cookies")用法一致
session.request() # 作用更上面一致,get会调用request方法
# 会保持当前网页的链接
session.prepare_request() # 保存操作
session.close() # 关闭请求连接,取消保存操作,结束session使用
print()
#header排序问题
def test_header(self):
session = requests.Session()
resposne = session.get(url=url2)
print(resposne.text)
headers = {
"Accept": "",
"Accept-Encoding": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Cache-control": "max-age=0",
"Connection": "keep-alive",
"Cookie": "BDUSS=VY0NFJ3eWJq"
}
print()
print(resposne.headers)
print(resposne.request.headers) # 返回内容没有请求头会自动添加,header的默认值
# response返回的header会自动处理进行排序后展现出来,字母顺序排序,会执行collections.OrderedDict进行排序
# 取消返回的headers排序
session.headers.clear() # 执行clear会取消排序,取消执行OrderedDict
session.headers.update()
resposne = session.get(url=url2)
print(resposne.text)
print(resposne.request.headers)
json与data使用
import requests
import json
def test_edit_flag():
url="http://127.0.0.1:5000/post"
url2="http://httpbin.org/post"
data = {"tag":{"id":134,"name":"guangdong"}}
str_data = json.dumps(data)
#json.loads() json字符串转换为str字符串
#json.dumps() str字符串转换为json字符串
res = requests.post(url = url,data=str_data)
print(res.json())
res = requests.request(method='POST',url=url)
print(type(json.dumps(res.json())),type(res.json()))
# data传参:报文是str类型,默认Content-type:text/plain
#2、json传参:报文是dict也可以是str类型,默认Content-Type:application/json
#看接口报文头content-type判断是什么传参
#data:可以传简直对的dict(非嵌套的字典dict),可以传递str格式
#json
pytest测试框架
pytest简介
支持参数化可以细分控制测试用例、支持简单的单元测试和复杂的功能测试、还支持selenium/appium等自动化测试、接口自动化测试
支持第三方插件,可以自定义扩展、pytest-html(
生成测试报告)、pytest-rerunfailures(失败case重复执行)
测试用例的skip和xfail处理
pytest插件
pytest-html 生成html报告
pytest-xdist 多线程插件
pytest-ordering 控制测试用例执行顺序
pytest-returnfailures 失败重跑
pytest-allure 生成测试报告
pytest脚本文件的文件名要带test名称,否则不会识别为测试用例也不会被pytest进行执行
默认测试用例规则:
1、模块使用test_开始
2、测试类必须以test开头
3、测试类必须使用test开头
安装方式
#在项目内打开终端
pip install --upgrade pip#升级pip命令,以防万一
pip install pytest/pytest-html/pytest-xdist/pytest-ordering/pytest-returnfailures/allure-pytest
运行方式
命令行
#pytest,在项目目录建立终端执行命令
pytest -vs #-v输出更加详细的信息 -s输出调试信息
-n 3#多线程执行 用例线程分开执行 前提是安装pytest-xdist插件,后面跟线程个数
--reruns 3 #失败用例重跑 跟重跑次数 执行后测试报告出现RR表示重跑了
--html #生成测试报告pytest --html=./requests/request.html 目录
主函数模式
#创建名字随意的py脚本,执行
#或直接放在要测试的文件最下面就行
import pytest
if __name__='__main__':
pytest.main()
#pytest.main(["testclass","test.py"]) 要用双引号传递要测试文件名称带后缀,如果时本文件也含有测试的类,记得添加要测试的类
#有这个就直接运行当前脚本就可以了,前提是配置正确
基于pytest.ini的配置文件
;创建名为pytest.ini的文件
[pytest]
addopts = -n
testpaths = ./test
;pytest测试位置
python_files = test*.py
;支持改变pytest的测试用例执行规则
;python_files = test*.py #修改执行测试用例文件的规则
;python_classes = Test* #修改执行的测试用文件里的测试类的规则
;python_functions = test_* #修改修改执行的测试用文件里的测试类的方法的规则
;用于表示注册格式标记,防止拼写错误,区分不同测试方式,冒烟
markers =
smoke: Run the smoke test
content: Run the content test
增加标记后marker,可以通过如下方式只执行测试有标记的函数(测试用例)
#测试文件 test_json_request.py,测试标记使用
import pytest
import requests
import json
# apikey : 3dfb3c34392b82dbd90ccb599388c30b
@pytest.mark.smoke
def test_edit_flag():
url = "http://127.0.0.1:5000/post"
url2 = "http://httpbin.org/post"
url3 = "https://api.apiopen.top/getSingleJoke?sid=28654780"
url_search = "https://json.apiopen.top/EmailSearch"
data = {"tag": {"id": 134, "name": "guangdong"}}
str_data = json.dumps(data)
res = requests.post(url=url, data=str_data)
# print(res.json())
data = {
"message": "失败"
}
res = requests.request(method='POST', url=url3, data=data)
print(res.json())
print()
print(res.text)
print()
print(res.json().get("message"))
@pytest.mark.smoke
def test_log():
url = "https://api.apiopen.top/loginUser"
data = {
"apikey": "3dfb3c34392b82dbd90ccb599388c30b",
"name": "prince1",
"passwd": "123456"
}
res = requests.request(method='post', url=url, data=data, params=data)
print(res.text)
# @pytest.mark.smoke
def test_regist_user():
url = "https://api.apiopen.top/registerUser"
data = {
"apikey": "3dfb3c34392b82dbd90ccb599388c30b"
, "name": "prince1",
"passwd": "123456"
}
res = requests.request('post', params=data, url=url)
print(res.text)
def programregist():
url = "https://api.apiopen.top/developerRegister"
data = {
"name": "Prince",
"passwd": "123456",
"email": "3279358204@qq.com"
}
# 3dfb3c34392b82dbd90ccb599388c30b
res = requests.request('post', url=url, params=data)
print(res.text)
# print(type(json.dumps(res.json())),type(res.json()))
test_edit_flag()
test_regist_user()
test_log()
#配置文件ini使用上面的
#terminal执行pytest test_json_request.py -m smoke,这样不会执行第二个测试函数
测试用例的前后置、夹具
执行测试用例前所需要进行的操作,加载网页,拿取数据
必定执行操作
#实际测试生效前提是函数有@pytest弄一个标签注册一下(@pytest.mark.smoke),然后pytest执行测试用例时指定使用该标签(-m smoke)才能正常使用
#在pytest中对于test类,支持固有函数分别在用例前执行和用例后,执行测试类前测试后一定执行
#名称固定,用于执行用例前的一些操作
class Test:
#def setup(self): #名称固定,用于执行用例前的一些操作
# print("用例前执行set up")
#def teardown(self): #名称固定,用于执行用例后的一些操作,关闭时数据库连接,断开连接等
def setup(self):
print("用例前执行setup")
def teardown(self):
print("用例后执行teardown")
def setup_class(self):
print("每个类之前执行setup_class")
def teardown_class(self):
print("每个类之后执行teardown_class")
自定义执行操作@pytest.fixture
@pytest.fixture装饰器可以实现部分用例的前后置
#实际测试生效前提是函数有@pytest弄一个标签注册一下(@pytest.mark.smoke),然后pytest执行测试用例时指定使用该标签(-m smoke)才能正常使用
@pytest.fixture(scope="",params="",autouse="",ids="",name="")
#scope作用域
function,class,module,package/session
#session 所有方法、类
#scope=function那麽可以在用例参数后面单独调用
#scope=class可以在类上面通过@pytest.mark.usefixture("函数名称")调用
#autouse 自动执行 True
@pytest.fixture(scope="class")修饰方法包夹
@pytest.mark.usefixtures("exe_classfixture")修饰方法
fixture一般结合conftest.py进行调用,不需要手动调用,必须叫conftest.py否则不识别会报错
实例演示
#fixture的conftest.py
#!/usr/bin/python
# _*_ coding: UTF-8 _*_
import requests
import pytest
@pytest.fixture(scope="function")
def exe_sql():#记得传参啊
print()
print("假设用例执行之前执行sql操作1")
yield
print("假设用例执行之后操作2")
print()
@pytest.fixture(scope="function")
def exe_sql2():#记得传参啊
print()
print("假设用例执行之前执行sql操作3")
yield
print("假设用例执行之后操作4")
print()
@pytest.fixture(scope="class")
def exe_classfixture():
print()
print("scop=class 的fixture测试,执行前")
yield
print("scop=class 的fixture测试,执行后")
print()
#测试文件 test_json_request.py
import pytest
import requests
import json
@pytest.mark.usefixtures("exe_classfixture")
class Testclass:
# apikey : 3dfb3c34392b82dbd90ccb599388c30b
def setup(self):
print()
print("用例前执行setup")
def teardown(self):
print("用例后执行teardown")
print()
def setup_class(self):
print()
print("每个类之前执行setup_class")
def teardown_class(self):
print("每个类之后执行teardown_class")
print()
@pytest.mark.smoke
def test_edit_flag(self,exe_sql2,exe_sql):
url = "http://127.0.0.1:5000/post"
url2 = "http://httpbin.org/post"
url3 = "https://api.apiopen.top/getSingleJoke?sid=28654780"
url_search = "https://json.apiopen.top/EmailSearch"
data = {"tag": {"id": 134, "name": "guangdong"}}
str_data = json.dumps(data)
res = requests.post(url=url, data=str_data)
# print(res.json())
data = {
"message": "失败"
}
res = requests.request(method='POST', url=url3, data=data)
print(res.json())
print()
print(res.json().get("message"))
@pytest.mark.smoke
def test_log(self):
url = "https://api.apiopen.top/loginUser"
data = {
"apikey": "3dfb3c34392b82dbd90ccb599388c30b",
"name": "prince1",
"passwd": "123456"
}
res = requests.request(method='post', url=url, data=data, params=data)
print(res.text)
@pytest.mark.smoke
def test_regist_user(self):
url = "https://api.apiopen.top/registerUser"
data = {
"apikey": "3dfb3c34392b82dbd90ccb599388c30b"
, "name": "prince1",
"passwd": "123456"
}
res = requests.request('post', params=data, url=url)
print(res.text)
def programregist(self):
url = "https://api.apiopen.top/developerRegister"
data = {
"name": "Prince",
"passwd": "123456",
"email": "3279358204@qq.com"
}
# 3dfb3c34392b82dbd90ccb599388c30b
res = requests.request('post', url=url, params=data)
print(res.text)
# print(type(json.dumps(res.json())),type(res.json()))
@pytest.mark.smoke
def test_fixture(exe_sql):
print("测试fixture")
# test05 = Testclass()
# test05.test_edit_flag()
# # test05.test_regist_user()
# test05.test_log()
# test_fixture()
if __name__ == "__main__":
pytest.main(["Testclass"])#要使用双引号传递测试类
# programregist()
[pytest]
addopts = -m smoke
testpaths = ../pytest
;python_files = test_*.py
markers =
smoke: Run the smoke test
content: Run the content test
============================= test session starts =============================
collecting ... collected 4 items
test_json_request.py::Testclass::test_edit_flag
test_json_request.py::Testclass::test_log
test_json_request.py::Testclass::test_regist_user
test_json_request.py::test_fixture
每个类之前执行setup_class
scop=class 的fixture测试,执行前
假设用例执行之前执行sql操作3
假设用例执行之前执行sql操作1
用例前执行setup
PASSED [ 25%]{'code': 200, 'message': '成功!', 'result': {'sid': '28654780', 'text': '这难道是传说中的脸刹?', 'type': 'video', 'thumbnail': 'http://wimg.spriteapp.cn/picture/2018/0927/5bacc729ae94b__b.jpg', 'video': 'http://wvideo.spriteapp.cn/video/2018/0927/5bacc729be874_wpd.mp4', 'images': None, 'up': '99', 'down': '7', 'forward': '3', 'comment': '9', 'uid': '12745266', 'name': '赵菓菓', 'header': 'http://wimg.spriteapp.cn/profile/large/2018/08/14/5b721ea4242da_mini.jpg', 'top_comments_content': None, 'top_comments_voiceuri': None, 'top_comments_uid': None, 'top_comments_name': None, 'top_comments_header': None, 'passtime': '2018-09-30 02:55:02'}}
成功!
用例后执行teardown
假设用例执行之后操作2
假设用例执行之后操作4
用例前执行setup
PASSED [ 50%]{"code":400,"message":"登陆信息有误,请检查用户名及密码!","result":null}
用例后执行teardown
用例前执行setup
PASSED [ 75%]{"code":400,"message":"用戶已存在!","result":null}
用例后执行teardown
scop=class 的fixture测试,执行后
每个类之后执行teardown_class
假设用例执行之前执行sql操作1
PASSED [100%]测试fixture
假设用例执行之后操作2
============================== 4 passed in 0.79s ==============================
Process finished with exit code 0
Yaml文件使用
yaml文件用来对获取的数据进行封装存储
#首先新建一个common文件夹,然后新建yaml_util.py文件,记得先新建一个yaml文件用于存储数据,位置在common外面
#yaml_util.py 固定写法
import os
import yaml
#读取
def read_yaml():
with open(os.getcwd()+'./extract.yaml') as f:
value = yaml.load(stream=f,Loader=yaml.FullLoader)
return value
#写入
def write_yaml(data):
with open(os.getcwd()+'./extract.yaml',mode='a',encoding='utf-8') as f:
value = yaml.dump(data,stream=f,allow_unicode=True)
print("yaml write done")
#清空
def clear_yaml():
with open(os.getcwd()+'./extract.yaml',mode='w',encoding='utf-8') as f:
f.truncate()
#记得新建一个__init__.py文件,使common变为可导入的package
#__init__.py
__all__ = ['yaml_util']
#test_json_request.py
#!/usr/bin/python
# _*_ coding: UTF-8 _*_
# 王子元 python学习
import pytest
import requests
import json
import common.yaml_util
@pytest.mark.usefixtures("exe_classfixture")
class Testclass:
# apikey : 3dfb3c34392b82dbd90ccb599388c30b
def setup(self):
print()
print("用例前执行setup")
def teardown(self):
print("用例后执行teardown")
print()
def setup_class(self):
print()
print("每个类之前执行setup_class")
def teardown_class(self):
print("每个类之后执行teardown_class")
print()
@pytest.mark.smoke
def test_edit_flag(self,exe_sql2,exe_sql):
url = "http://127.0.0.1:5000/post"
url2 = "http://httpbin.org/post"
url3 = "https://api.apiopen.top/getSingleJoke?sid=28654780"
url_search = "https://json.apiopen.top/EmailSearch"
data = {"tag": {"id": 134, "name": "guangdong"}}
str_data = json.dumps(data)
res = requests.post(url=url, data=str_data)
# print(res.json())
data = {
"message": "失败"
}
res = requests.request(method='POST', url=url3, data=data)
print(res.json())
print()
#用于使用yaml的写入方法将获取的json内容写入进去
common.yaml_util.write_yaml({"message2":res.json().get("message")})
# print(common.yaml_util.read_yaml())
@pytest.mark.smoke
def test_log(self):
url = "https://api.apiopen.top/loginUser"
data = {
"apikey": "3dfb3c34392b82dbd90ccb599388c30b",
"name": "prince1",
"passwd": "123456"
}
res = requests.request(method='post', url=url, data=data, params=data)
common.yaml_util.write_yaml({
" ":" ",
"code":res.json().get("code"),
"message"+" https://api.apiopen.top/loginUser":res.json().get("message"),
"result"+" https://api.apiopen.top/loginUser":res.json().get("result")
})
print(res.text)
# print(common.yaml_util.read_yaml())
@pytest.mark.smoke
def test_regist_user(self):
url = "https://api.apiopen.top/registerUser"
data = {
"apikey": "3dfb3c34392b82dbd90ccb599388c30b"
, "name": "prince1",
"passwd": "123456"
}
#还是写入数据
res = requests.request('post', params=data, url=url)
common.yaml_util.write_yaml({
" ":" ",
"code"+" https://api.apiopen.top/registerUser":res.json().get("code"),
"messag"+" https://api.apiopen.top/registerUser":res.json().get("message"),
"result"+" https://api.apiopen.top/registerUser":res.json().get("result")
})
# print(res.text)
# print(common.yaml_util.read_yaml())
def programregist(self):
url = "https://api.apiopen.top/developerRegister"
data = {
"name": "Prince",
"passwd": "123456",
"email": "3279358204@qq.com"
}
# 3dfb3c34392b82dbd90ccb599388c30b
res = requests.request('post', url=url, params=data)
# print(res.text)
# print(type(json.dumps(res.json())),type(res.json()))
@pytest.mark.smoke
def test_fixture(exe_sql):
print("测试fixture")
# test05 = Testclass()
# test05.test_edit_flag()
# # test05.test_regist_user()
# test05.test_log()
# test_fixture()
if __name__ == "__main__":
pytest.main(["Testclass"])#要使用双引号传递测试类
# programregist()
#conftest.py文件 添加一个清除yaml文件的函数
#!/usr/bin/python
# _*_ coding: UTF-8 _*_
import requests
import pytest
import common
@pytest.fixture(scope="function")
def exe_sql():#记得传参啊
print()
print("假设用例执行之前执行sql操作1")
yield
print("假设用例执行之后操作2")
print()
@pytest.fixture(scope="function")
def exe_sql2():#记得传参啊
print()
print("假设用例执行之前执行sql操作3")
# common.yaml_util.clear_yaml()
yield
print("假设用例执行之后操作4")
print()
@pytest.fixture(scope="session",autouse=True)
#范围为当前所有方法和类,自动执行,防止上一次测试存储的yaml文件数据留存
def clear_yaml_data():
common.yaml_util.clear_yaml()
#清理yaml文件上一个测试输入内容
@pytest.fixture(scope="class")
def exe_classfixture():
print()
print("scop=class 的fixture测试,执行前")
yield
print("scop=class 的fixture测试,执行后")
print()
接口自动化封装
规避用例未按照计划操作,自身多次重复执行
解决方式使用YAM文件代替保存
基本展示
'''
1、定义reverse函数、源代码
2、定义了两个test_reverse函数
3、在该目录下打开命令行,pytest xxx.py 运行指定的py文件
4、直接运行pytest而不添加脚本名称,会吧所有带test的名字的文件执行一遍,
5、只有带test的文件才会被收集test_xxx()的函数或者方法作为测试用例执行
6、断言使用assert,是python自带的断言方式,不属于pytest的
7、当测试用例有多个验证点,其中某一个验证点一旦不通过,后面的验证点都不会再执行.
8、pytest测试验证点是按顺序执行的,包括顺序执行测试函数顺序,所以重要的函数要放在前面先进行执行,重要的断言也要写在前面
'''
def reverse(str):
return str[::-1] #倒序 输出
s = 'python'
s = [1,2,3,4]
def test_resverse():
assert reverse('god') == 'dog'
def test_resverse02():
assert reverse('456') == 'dog' #执行测试用例这个会不通过
fixture测试环境准备,测试数据准备
[
{"name": "tony","password": "password"},
{"name": "tony2","password": "password2"},
{"name": "tony3","password": "password3"}
]
//新建一个名为users_dev.json的文件
'''
执行顺序:
1、pytest找到test开头或结尾py文件,再去找到以test开始的方法,然后执行这个test方法做测试
2、执行该方法发现传入参数里有跟fixture同名的users
3、pytest认定users是fixture,执行users方法,读取json文件解析成python对象
4、开始真正执行test_user_pwd方法,users返回值传入该方法
5、测试环境准备、数据准备
'''
#新建一个有test的py脚本
#fixture的使用
import pytest
strfile = 'C:\\ProgramEdit\\pythonProject\\202112245_03\\pytest\\users_dev.json'#你的users_dev.json存储文件位置
class TestUserPassword:
@pytest.fixture()
def users(self):
return json.loads(open(strfile,'r').read())
def test_user_pwd(self,users):
with pytest.raises(AssertionError):
for user in users:
pwd = user['password']
assert len(pwd)>=10#这么写测试用例是不通过的,会报AssertionError说我们断言错误,但这么写执行用例时不会报错
#因为我们规定了可以出现AssertError异常,所以pytest test_03_fixture.py能够成功执行
# class TestUserPassword:
# @pytest.fixture()
# def users(self):
# return json.loads(open(strfile,'r').read())
# def test_user_pwd(self,users):
# for user in users:
# pwd = user['password']
# assert len(pwd)>=5 #这个是不报异常可以的执行语句记得控制台执行测试脚本时候尝试一下
# class TestUserPassword:
# @pytest.fixture()
# def users(self):
# return json.loads(open(strfile,'r').read())
# def test_user_pwd(self,users):
# for user in users:
# pwd = user['password']
# assert len(pwd)>=10 #这个是执行报错的记得注意一下
pytest测试用例管理框架
默认测试用例规则:
1、模块使用test_开始
2、测试类必须以test开头
f,users):
with pytest.raises(AssertionError):
for user in users:
pwd = user[‘password’]
assert len(pwd)>=10#这么写测试用例是不通过的,会报AssertionError说我们断言错误,但这么写执行用例时不会报错
#因为我们规定了可以出现AssertError异常,所以pytest test_03_fixture.py能够成功执行
# class TestUserPassword:
# @pytest.fixture()
# def users(self):
# return json.loads(open(strfile,'r').read())
# def test_user_pwd(self,users):
# for user in users:
# pwd = user['password']
# assert len(pwd)>=5 #这个是不报异常可以的执行语句记得控制台执行测试脚本时候尝试一下
# class TestUserPassword:
# @pytest.fixture()
# def users(self):
# return json.loads(open(strfile,'r').read())
# def test_user_pwd(self,users):
# for user in users:
# pwd = user['password']
# assert len(pwd)>=10 #这个是执行报错的记得注意一下
## pytest测试用例管理框架
默认测试用例规则:
1、模块使用test_开始
2、测试类必须以test开头