【ZJ】Pytest接口自动化框架搭建(完,待整理)

  • 时间:2025-11-27 22:17 作者: 来源: 阅读:6
  • 扫一扫,手机访问
摘要:1.Pytest框架搭建 1.测试框架 java:junit,testing python:unittest,pytest 2.快速上手 安装 pip install pytest 升级到最新版 pip install pytest -U pytest启动的三种方式 命令:pytest代码 建立了test_login文件后 def test_login print('执行l

1.Pytest框架搭建

1.测试框架

java:junit,testing
python:unittest,pytest

2.快速上手

安装
pip install pytest
升级到最新版
pip install pytest -U
pytest启动的三种方式

命令:pytest代码
建立了test_login文件后

def test_login
	print('执行login测试用例')

在启动文件main.py


import pytest
pytest.main()

3.鼠标(不推荐),因为是python提供的,不是pytest

3.看懂测试结果

执行环境:版本,根目录,用例数量
执行过程:文件名称,用例结果,执行进度
失败详情:用例内容,断言提示
整体摘要:结果情况,结果数量,花费时间

其中用例结果有6种

4.用例规则

用例发现规则
测试框架在识别,加载用例的过程,称之为:用例发现
pytest的用例发现规则步骤

遍历所有的文件,除了:.开头的文件,.开头的目录打开python文件,test_开头或者_test结尾遍历所有Test开头类收集所有的test_开头的函数或者方法
用例的内容规则
pytest8.4增加一个强制的要求可调用的(函数,方法,类,对象)名字test_开头没有参数(参数有另外的含义)没有返回值(默认为None)

案例


#测试函数"""
def add(a,b)
	return a+b
	
class TestAdd:
#测试方法"""
def test_int(self):
	res=add(1,3)
	assert res == 4

def test_str(self):
	res=add("1","3")
	assert res == "13"
	
def test_list(self):
	res=add([1],[2,3,4])
	assert res == [1,2,3,4]

5.配置框架

配置可以改变pytest默认的规则

命令参数ini配置文件
所有的配置方式,可以一键获取
pytest -h

有哪些配置
分别是什么方式

-开头:参数小写字母开头:ini配置大写字母开头:环境变量
配置文件:pytest.ini

常用参数:

6.标记mark

标记,可以让用例与众不同,进而可以让用例被区别对待

1.用户自定义标记
用户自定义标记只能实现用例筛选
步骤:
1.先注册
如:在pytest.ini文件中


[pytest]
marker =
	api:接口测试
	web:web测试
	ut:单元测试
	login:登录相关
	pay:支付失败
	ddt:数据驱动测试

2.再标记
如:


#测试函数"""

def add(a,b)
	return a+b

class TestAdd:
#测试方法"""
@pytest.mark.web	
def test_int(self):
	res=add(1,3)
	assert res == 4
@pytest.mark.api
def test_str(self):
	res=add("1","3")
	assert res == "13"
@pytest.mark.pay	
def test_list(self):
	res=add([1],[2,3,4])
	assert res == [1,2,3,4]

3.后筛选

pytest -m web
结果:只执行了标记了web的测试用例

2.框架内置标记
框架内置标记为用例增加特殊执行效果
和用户自定义标记区别

不需注册,可以直接使用不仅可以筛选,还可以增加特殊效果不同标记,增加不同的特殊效果

案例

@pytest.mark.skip	
def test_list(self):
	res=add([1],[2,3,4])
	assert res == [1,2,3,4]

结果:跳过了这条测试用例

7.数据驱动测试参数

数据驱动测试=参数化测试+数据文件
根据数据文件的内容,动态决定用例的数量,内容
1.创建一个data.csv


a,b,c
1,2,3
2,3,4
4,5,6
1,2,4

2.封装函数,数据文件,驱动用例数量和内容


import pytest	
#封装函数
import csv
	def read_csv(path):
		reader=csv.reader(f)
		return list(reader)[1:]

#测试函数"""		
def add(a,b)
	return a+b
#测试用例"""	
class TestAdd:
	@pytest.mark.ddt
	@pytest.mark.parametrize(
	"a,b,c"
	read_csv("data.csv")
	)	
	def test_list(self,a,b,c):
		res=add(int(a),int(b))
		assert res == c

8.夹具fixture

1.夹具:在执行之前,执行之后运行代码
场景:
之前:加密参数、之后:解密结果
之前:启动浏览器、之后:关闭浏览器
之前:注册,登录账号、之后:删除账号

1.创建函数
2.添加装饰器
3.添加yield关键字


@pytest.fixture
def f():
	print(datatime.now(),'用例开始执行')
	#前置操作
	yield
	#后置操作
	print(datatime.now(),'用例结束执行')

2.使用fixture
1.在用例参数列表,加入fixtures名字就可以了
2.给用例加上usefixtures标记


@pytest.fixture
def f():
	print(datatime.now(),'用例开始执行')
	#前置操作
	yield
	#后置操作
	print(datatime.now(),'用例结束执行')
#使用方式1
@pytest.mark.usefixtures("f")
def test_1():
	pass
#使用方式2
def test_2(f):
	pass

3.高级用法
1,现实使用


@pytest.fixture(autouse=True)
def f():
	print(datatime.now(),'用例开始执行')
	#前置操作
	yield
	#后置操作
	print(datatime.now(),'用例结束执行')

def test_1():
	pass
#使用方式2
def test_2():
	pass

2.依赖使用

linux:使用linux进行编辑git:使用git进行版本控制fixture:使用fixture来进行前后置自动操作

import pytest
@pytest.fixture
def ff():
	print('我也是夹具,但是被fixture使用')

@pytest.fixture(autouse=True)
def f(ff):
	print(datatime.now(),'用例开始执行')
	#前置操作
	yield
	#后置操作
	print(datatime.now(),'用例结束执行')

def test_1():
	pass
#使用方式2
def test_2():
	pass

运行结果

3.返回内容:接口自动化封装:接口关联
如:用yield传递参数


@pytest.fixture(autouse=True)
def f():
	print(datatime.now(),'用例开始执行')
	#前置操作 yield传递参数123
	yield 123
	#后置操作
	print(datatime.now(),'用例结束执行')

def test_1(f):
	print('收到传递的参数',f)
#使用方式2
def test_2():
	pass

4.范围共享:
默认范围:function
全局范围:session
使用conftest.py

9.插件管理

pytest 插件生态是pytest的优势之处
插件分为两类
1.不需要安装:内置chajian
2.需要安装:第三方插件

插件的启用和管理
1、启用: -p abc
2,禁用:-p no:abc

插件的使用方式:

参数配置文件fixturemark

10.常用的第三方插件

网址:docs.pytest.org/stable/refernce/plugin_html

pytest-html
作用:生成html的测试报告
安装:
pip install pytest-html
使用:
pytest --html=report:file:///E:/PyProject/my_pytest/report.html
也可以在配置文件按添加,如下:

pytest-xdist
用途:分布式执行
安装:pip install pytest-xdist
使用:
-n N
n:线程
N:表示几个分布式使用
只有在任务本身耗时比较长,超出调用成本很多时候,才有意义
分布式执行,有并发问题:资源竞争、乱序

pytest-reunfailures
用途:用例失败后,重新执行
安装
pip install pytest-reunfailure
使用:
--reruns 5 --reruns-delay 1
pytest-result-log
用途:把用例的执行结果记录到日志文件中
安装:pip install pytest-result-log
使用:

11.企业级测试报告
allure是一个测试报告框架
pip intsall allure-pytest
安装
pip install allure-pytest
配置
--alluredir=temps --clean-allure
生成报告
allure generate -o report -c temps

allure 支持对用例进行分组和关联(敏捷开发术语)


@allure.epic 史诗 项目
@allure.feature 主题 模块
@allure.story 故事 功能
@allure.title 标题 用例

使用相同装饰器的用例,自动并入一组

12.web自动化测试实战

pytest 仅进行用例管理,不会控制浏览器,需要借助新的工具:selenium

1.只了解selenium
2.搜索关于selenium的pytest插件
案例


@pytest.fixture()
def selenium():
	d=webdriver.Chrome()
	yield d
	d.quit()

13.测试框架的封装

封装:隐藏细节,增加功能,优化功能
接口自动化:
1.可以用yaml作为用例,降低门槛
2.自动请求接口,断言接口
3.自动在日志记录http报文
4.自动生成allure测试报告

yaml文件 test_yaml


---
name:访问百度
steps:
	- request:#发送请求
		method:GET
	- url:https://www.baidu.com
	
	- response:#断言响应
		status_code: 200
		text:'**baidu**'
		json:
			code:0
			data:
				banner_list:
					- name: 美酒
	
	- extract:#提取变量
		code:[ status_code,(.*)]
	- validate:# 断言变量
		code:200

14.yaml文件格式

一句话:完全兼容json格式,并且支持python相似写法
重点:
1.yaml完全兼容json
2,数据格式不是变成语言
3,像python一样容易编辑和阅读
1.安装yaml模块
pip install pyyaml
2.编写yaml文件
1.#作为注释符号
2.缩进:2个空格
3.成员表示:- 表示列表成员 :表示字典成员
4.完全兼容json格式
benfan.yaml


数字:
	- 1
	- -1
	- 1.1
字符串:
	-'121221'
	- "aadd"
空值:null #json 写法
列表:[1,2,3,] #json写法
字典:{"a":1,"b":2} #json 写法

3.加载yaml文件
benfan.py


import yaml

def load_yaml(path):
	f= open("beifan.yaml",encoding="utf-8")#打开文件
	s=f.read()#读取文件内容
	data=yaml.safe_load(s)
	return data

15.接口测试用例

1.设计用例内容
1.名字:请求首页数据接口
2.标记【可选】
3.步骤
3.1请求接口:GET https://www.baidu.com
3.2响应断言: status_code ==200
3.3提取变量: json()[‘code’]
yaml用例
test_api


name:登录成功的测试用例
steps:
- request:#发生请求
	method:POST
	url:http:///116.62.63.211/shop/api.php?application=app&application_client_type=wexin
	params:
		s:user/login
	json:{
		"accounts":"beifan_1105",
		"pwd":"beifan_1105",
		"type":"username"
		}
	-reponse:#断言响应
		status_code:200
		json:
			code:0
			msg:登录成功
		data:
			username:beifan_1105
			
	-extract:#提取变量
		token:[json,$.data.token]

16.封装接口自动化框架

1.请求接口
外部工具:requests
HTTP协议抓包角度,请求由三部分组成:
行:方法+地址(必填)
头:请求头(键值对)
体:参数内容

request.py


import requests
###1.行
url='http:///116.62.63.211/shop.api.php'
#GET 方法
request.get(url)
#post方法
requests.post(url)
#任意方法
requests.request('MOVE',url)


###2.头
method='post'
url='http://116.62.63.211/shop/api.php'
requests.request(
	method,url,
	headers={#字符串字典
	"1":"a",
	"2":"b",
	}
)

###3.参数
method='post'
url='http://116.62.63.211/shop/api.php'
requests.request(
	method,url,
	json={#任意内容字典
	"a":1,
	"b":[1,2,3],
	"c":{},
	}
)

断言响应
1.响应里有什么
2.响应如何断言

从http协议抓包的角度,响应由三个部分构成
行:状态码
头:响应头(键值对)
体:响应内容


#resp 就是响应
#获取响应的内容
print(resp.status_code)#状态码
print(resp.headers)#响应头
print(resp.text)#响应正文
print(resp.json())#响应正文转成json
#断言单个内容是否正确
assert resp.station_code==200
assert '美酒' in resp.text
assert resp.json()['data']['banner_list'][0]["name"]=="美德"
#断言全部内容
from responses_validator import validator
validator(
	resp,
	status_code=200,
	text="*美酒*",
	json={
		"data":{
			"banner_list":[{"name":"美酒"}]
		}
	}
)




3.变量提取
基本原则:

JSON:JSONPTHHTML:XPATH字符串:RE
RE可以兜底

案例:封装到extract_utills.py


import jsonpath
#['data']['banner_list'][0]["name"]
def extract(resp,attr_name,exp):
	try:
		resp.json=resp.json()
	except Exception:
		resp.json={}
		
	attr=getattr(resp,attr_name)
	res=jsonpath.jsonpath(attr,exp)
	return res[0]

在request.py导入


from extract_utills import extract
var=extract(resp,'json','$..banner_list[0].name')
print(var)

4.框架落地封装

入口文件main.py


import os
import pytest
pytest.main()
os.system(f"allure generate -c -o report temps")

配置文件pytest.ini

tests文件夹
包含test_api.yaml和test_yaml.py
其中test_api.yaml


name:登录成功的测试用例
steps:
- request:#发生请求
	method:POST
	url:http:///116.62.63.211/shop/api.php?application=app&application_client_type=wexin
	params:
		s:user/login
	json:{
		"accounts":"beifan_1105",
		"pwd":"beifan_1105",
		"type":"username"
		}
	-reponse:#断言响应
		status_code:200
		json:
			code:0
			msg:登录成功
		data:
			username:beifan_1105
			
	-extract:#提取变量
		token:[json,$.data.token]

其中test_yaml.py


import allure
from commons.runner_utills import runner
from commons.yaml_utills import load_yaml
def test_yaml():
	my_var={}
	data=load_yaml("tests/test_api.yaml")
	allure.title(data['name'])
	for step in data['steps']:
		for k,v in step.items():
			runner(k,v,my_var)

commons文件
extract_utills.py


import jsonpath
#['data']['banner_list'][0]["name"]
def extract(resp,attr_name,exp):
	try:
		resp.json=resp.json()
	except Exception:
		resp.json={}
		
	attr=getattr(resp,attr_name)
	res=jsonpath.jsonpath(attr,exp)
	return res[0]

runner_utils.py


import requests
import responses_validator
from commons.extrat_utills import extract
import logging

logger=logging.getLogger("beifan")


def runner(k,v,my_var):
	match k:
		case 'request' :#发送请求
			logger.info('1.正在发送请求。。。')
			logger.info(f'{v}')
			my_var['resp']=requests.request(**v)#字典使用两颗星
		case 'reponse' :#断言响应
			logger.info('2.正在响应断言。。。')
			logger.info(f'{v}')
			responses_validator.validator(my_var['resp'],**v)#字典使用两颗星
		case 'extract' :#变量提取
			logger.info('3.正在提取变量。。。')
			for var_name,var_exp in v.items():
				value=extract(my_var['resp'],*var_exp)#列表使用一个星
				logger.info(f"{var_name}	={value}")

#查询数据库。。。

进一步完善
1.YAML用例测试文件上传?
2.YAML用例进行数据去上传?
3.YAML用例进行自定义断言?
4.YAML用例进行数据库查询?
两个方向:1.自动化工程师2.测试开发工程师

2.pytest从入门到精通

1.安装和检验pytest

终端安装 pip pytest检验 pytest

2.基本用例要求

创建test_beifan.py


#1.创建test_开头的文件
#2.创建test_开头的函数
def test_abc():
		assert False
#3.在函数中使用断言 IF assert
#4.用例可以放在类中,类名字必须Test开头
class TestBeiFan:
	def test_abc(self):#实例方法的绑定参数self
		assert False
#5.用例文件可以位于任一目录,一般为test目录

3.mark-标记-只执行部分用例,用例的筛选

方法一:可以点击执行箭头执行
方法二:用mark筛选
创建test_1_beifan.py,目前加上上个文件有四个用例,对要执行的用例进行mark装饰


#1.创建test_开头的文件
#2.创建test_开头的函数
def test_abc():
		assert False
#3.在函数中使用断言 IF assert
#4.用例可以放在类中,类名字必须Test开头
class TestBeiFan:
#要执行的用例mark装饰
	@pytest.mark.first
	def test_abc(self):#实例方法的绑定参数self
		assert False
#5.用例文件可以位于任一目录,一般为test目录

在终端输入 pytest -m first执行了标记的用例

Q:但是如果标记多个要怎么去使用更便捷呢?
创建配置文件pytest.ini
步骤:注册–>使用–>筛选


[pytest]

markers =
	aaa
	bbb
	ccc
	first

使用的时候在用例在装饰器就好了,如: pytest.mark.first
执行直接在终端输入 pytest -m first
Q:mark的高级用法?不需要注册的用法
内置标记,特性:不需要注册,不仅用例筛选还有特殊效果

@pytest.mark.skip无条件跳过
@pytest.mark.xfail预期失败
@pytest.mark.parametrize("n",[1,2,3])改变用例执行数量和结果
其他:


-skipif 有条件跳过
-usefixture 自动使用fixture
-parameterize 参数化测试:动态的生成用例

4.fixture–夹具–用例-前置后置自动化操作

定义:
fixture:夹具,实现用例前置,后置的自动化操作,其中yield是生成器的意思,fixture是一个函数
原理:


def beifan():
	print(1)
	yield
	print(2)

g=beifan()
print('执行前置')
next(g)
print('执行用例')
print('执行后置')
next(g)

执行效果:

Q:怎么使用fixture?
步骤:添加装饰器 @pytest.fixture–>使用yield区分前后置


import pytest
#1.创建fixture
@pytest.fixture
def beifan()
	print('用例前置操作:连接数据库,启动浏览器,生成测试数据')
	yield
	print('用例后置操作:关闭数据库,退出浏览器,生成测试数据')
#2.使用名为befan的fixture
@pytest.mark.fixture
def test_abc(beifan):
	print('loading test add')
	assert 1 + 1 == 3
	#注意用例执行失败后面代码不会执行但会执行后置
	print('test ending')

执行效果:

Q:两个用例使用一个fixture这么设计代码?以防重复使用fixture
设置作用域,共享fixture。
优点:复用代码减少前置和后置的操作耗时。在用例之间传递数据。
缺点:在共享的范围内容,fixture不会重复执行

fixture的作用范围:

function ,默认,在函数的范围,进行共享class ,在同一类的范围,进行共享moudle,在同一模块(文件)范围,进行共享package,在同一包(目录)的范围,进行共享session,在同一此执行用例所有用例,进行共享

import pytest
#1.创建fixture,fixture的作用范围
#这里session和modle都可以
@pytest.fixture(scope='session')
def beifan()
	print('用例前置操作:连接数据库,启动浏览器,生成测试数据')
	yield
	print('用例后置操作:关闭数据库,退出浏览器,生成测试数据')
#2.使用名为befan的fixture
@pytest.mark.fixture
def test_abc(beifan):
	print('loading test add')
	assert 1 + 1 == 3
	#注意用例执行失败后面代码不会执行但会执行后置
	print('test ending')
#2.使用名为befan的fixture
@pytest.mark.fixture
def test_def(beifan):
	print('loading test add')
	assert 1 + 1 == 3
	#注意用例执行失败后面代码不会执行但会执行后置
	print('test ending')

Q:fixture在用例之间传递数据的用法


import pytest
#1.创建fixture,fixture的作用范围
@pytest.fixture(scope='session')
def beifan()
	print('用例前置操作:连接数据库,启动浏览器,生成测试数据')
	yield
	print('用例后置操作:关闭数据库,退出浏览器,生成测试数据')
#2.使用名为befan的fixture
#使用命名空间,字典的形式,断言之前
@pytest.mark.fixture
def test_abc(beifan,doctest_namespace):
#定义name
	name='beifan。。。'
	doctest_namespcae['name']=name


	print('loading test add')
	assert 1 + 1 == 3
	#注意用例执行失败后面代码不会执行但会执行后置
	print('test ending')

#2.使用名为befan的fixture
@pytest.mark.fixture
def test_def(beifan,doctest_namespace):
#取出name
	name=doctest_namespace['name']
	print(name)

	print('loading test add')
	assert 1 + 1 == 3
	#注意用例执行失败后面代码不会执行但会执行后置
	print('test ending')

3.pytest(基础资料)

pytest测试框架

单元测试框架定义:针对软件开发最小的单元(函数,方法)进行正确性位置测试

单元测试框架:java(junit,testing)python(unittest,pytest)

单元测试框架的作用

测试发现:从多个文件中找到测试用例测试执行:按照一定的顺序和规则去执行并且生成结果测试判断:通过断言判断预期结果和实际结果的差异测试报告:统计测试的结果,耗时,通过率等生成美观的测试报告

自动化框架的内容:单元测试框架,pom设计模式,数据驱动,关键字驱动,全局配置文件的封装,日志监控,selenium和request二次封装,断言,报告邮件,更多…

pytest简介和安装

pytest比unittest更灵活,容易上手pytest可以和selenium,reques,appnium结合实现web自动化,接口自动化,app自动化pytest可以实现测试用例的跳过和reruns失败用例重试pytest可以和allure生成测试报告pytest可以和jenkins持续集成pytest提供强大的插件

如下:建立requirements.txt 内容如下


pytest 本身
pytest-html 生成html格式化报告
pytest-xdist 测试用例分布式进行,多CPU分发
pytest-ordering 用于改变测试用例的执行顺序
pytest-rerunfailures 用例失败后重跑
allure-pytest 用于生成美观的测试报告

终端上输入安装:


 pip install -r requirements.txt

或者


pip pytest

检验:


pytest --version

pytest

模块命名

以test_开头或者 _test结尾测试类必须以Test开头并且不再有ini方法测试方法必须以test开头

测试用例的运行方式

1.主函数运行方式

(1)运行所有:pytest.main()


class  TestLogin:
    def test_01_login(self):
        print("测试登录")

if __name__ == '__main__':
    pytest.main()

(2)运行指定模块:


pytest.main(['-vs','test_login.py'])

(3)指定目录:


pytest.main(['-vs','./test_other.py'])

(4)通过nodeid指定用例运行:nodeid由模块名,分隔符,类名,函数名组成


pytest.main([’-vs‘,’./interface_testcase/test_interface.py::test_04_func‘])

2.命令行模式

(1)运行所有:


pytest

(2)指定模块:


pytest -vs test_login.py

(3)指定目录:


pytest -vs ./test_other.py

(4)指定目录:


pytest -vs ./interface_testcase/test_interface.py::test_04_func

3.参数详解

-v 输出更详细的信息
-s 输出调试信息

-vs:这两个参数一起用

-n:支持多线程或者分布式运行测试用例

如:pytest -vs ./test_login.py -n 2

–returns NUM:失败用例重跑

-x:表示只要要一个用例报错,那么测试就停止

–maxfall=2 出现两个用例失败就停止

-k:根据测试用例的部分字符串指定测试用例

如:pytest -vs/testcase -k"ao"


class  TestLogin:
    def test_01_login(self):
        print("测试登录")

if __name__ == '__main__':
    pytest.main(['-s'])

案例:创建一个all.py文件运行指定的test用例和控制输出的信息


import  pytest

if __name__ == '__main__':
    pytest.main(['-vs','test_login.py'])

相当于终端输入


pytest -vs test_login.py

3.结合pytest.ini全局配置文件执行

pytest为整个pytest的核心配置文件

位置:一般放在项目的根目录

编码:必须是ANSI编码格式,可以使用notepad++修改编码格式

作用:改变pytest默认行为

运行规则:不管是主函数的模式运行,命令行模式运行,都会去读取ini文件


1 [pytest]
2 #命令行参数
3 #常见:--html=./reports/report.html --reruns 2
4 addopts = -vs -m "user_manager or smoke"
5 #配置执行的用例位置
6 testpaths = ./testcases
7 #配置修改默认的模块规则
8 python_files = test_*.py
9 #配置修改默认的类规则
10 python_classes = Test*
11 #配置修改默认的用例规则
12 python_functions = test_*
13 #配置基础路径
14 base_url = http://www.baidu.com
15 #标记
16 markers =
17 smoke:冒烟测试用例
18 product_manage:商品管理
19 user_manager:用户管理

pytest 测试用例的顺序

区分

unittest:ascII的大小来绝对的执行顺序

pytest:默认从上到下

改变默认的执行顺序,使用mark标记

添加注释,其中1是执行的顺序


@pytest.mark.run(order=1)

如何分组执行(冒烟、分模块执行、分接口和web执行)

smoke:冒烟用例,分布在各个模块内

添加冒烟标记在.py文件


@pytest.mark.smoke

注意要在pytest.ini文件打一下标记


 #标记
 markers =
smoke:冒烟测试用例
product_manage:商品管理
user_manager:用户管理

在终端输入


pytest -vs -m "smoke"

想执行其他模块


pytest -vs -m "smoke and 其他模块的名称"

跳过执行的用例

无条件跳过


@pytest.mark,skipif(reason=’跳过原因‘)

有条件跳过


@pytest.mark,skipif(条件,reason=’跳过原因‘)

例如


@pytest.mark.skipif(age>=18,reason='已成年')

pytest框架的一些前后置(固件,夹具)处理,常用的三种

前后置:setup_class、setup、teardown、teardown_class


class TestPosition:
    def setUp_class(self):
        print('
 在每个类执行前的准备工作,例如:创建日志对象、数据库链接、接口请求对象')
    def setup(self):
        print('
 执行用例之前的开始动作,比如打开浏览器')



    def test_01(self):
        print('
 测试用例')

    def teardown(self):
        print('
 执行用例之前的扫尾动作,比如关闭浏览器')
    def teardown_class(self):
        print('
 在每个类执行前的准备工作,例如:销毁日志对象、数据库链接、接口请求对象')

使用@pytest.fixture()装饰器实现部分用例的前后置

装饰器:
@pytest.fixtrue(scope=“作用范围”,autouse=“自动执行”,params=“参数化”,ids=“参数别
名”,name=“固件别名”)

案例:


@pytest.fixture(scope="",params="", autouse="",ids="",name="")
def my_fixture():
    print('这是前置的方法,可以实现部分以及全部用例的前置')
    yield
    print('这是后置方法,可以实现部分以及全部用例的后置')

def test_02(self,my_fixture):
    print('
 执行fixture')

用法:

scope表示的是被@pytest.fixture标记的方法的作用域。function(默认),class,moudle,package/sessionparams:参数化(支持,列表[],元组(),字典列表[{},{},{}],字典元组({},{},{})

import pytest


@pytest.fixture(scope='function',params=['参数1','参数2','参数3'])
def my_fixture(request):
   print('前置')
   yield request.param 
   # 注意yield和return都表示返回的以上,但是return后面不能跟代码,yield可以
   print('后置')

def test_02(self,my_fixture):
    print('
 执行fixture')

注意:params和yield不能一起用但是可以和前置一起用。

autouse=True:自动化使用,默认False

ids:当使用params参数化时,给每一个值设置一个变量名,意义不大

name:给表示的是被@pytest.fixture标记的方法取一个别名

当取了别名之后,那么原来的名就用不了了

通过conftest.py和@pytest.fixture()结合使用实现全局的前置应用(如:项目的全局登录,模块的全局处理)

conftest.py文件是单独存放的一个夹具的配置文件,名称不能更改

用处可以在不同的py文件中使用同一个fixture函数

原则上conftest.py需要和运行的用例放在统一层,并且不需要做任何的import导入的操作

案例

全局拥有全局前置,各个子模块下面有一个前置,对于用例编写为


def test_01_testconftest(self,all_fixtuture,user_fixture)

print("test用例")

print(user_fixture)

print(all_fixture)

这样全局前置包含了子模块的前置

setup/teardown,setup_class/teardown_class 它是作用于所有用例或者所有的类

@pytest.fixture() 它的作用是局部的也是全局的

conftest.py和@pytest_fixture()结合使用,作用于全局的前后置

断言

assert

pytest结合allure+pytest插件生成allure测试报告

以前是使用的是pytest_html生成,现在可以使用allure+pytest结合生成美观的报告

1.下载、解压、配置path路径

https://github.com/allure-framework/allure2/releases

配置环境变量如:

D:allure-2.30.0in

验证

allure --version

2.加入命令到pytest.ini文件,生成json格式的临时报告


addopts = -vs --alluredir ./temp

3.生成allure报告

allure generate 命令,固定的

./temp 临时的json格式的报告

-o 输出output

./report 生成allure报告路径

.clear 清空./report路径原来的报告


if __name__ == '__main__':
    time.sleep(3)
    # pytest.main(['-vs','test_login.py'])
    pytest.main()
    # allure generate 命令,固定的;./temp 临时的json格式的报告 -o 输出output ./report 生成allure报告路径 .clear 清空./report路径原来的报告
    os.system('allure generate ./temp -o ./report --clear')

yaml接口自动化实战

1,断言的封装

2,allure报告的定制

3,关键字驱动和数据驱动结合实现接口自动化测试

4,python的反射

正常:先初始化对象,再调方法

反射:通过对象得到类对象,然后通过类对象调用方法

5,jenkins的持续集成和allure报告的集成,并且根据项目的自动化的报告错误率发送电子邮件

pytest接口自动化-基础篇

接口测试

发展背景:

目前市面上主流的接口测试工具:(适用于中小型项目)

postman+newman+git+jenkinsjmeter+ant+git+jenkins

目前主流的接口自动化测试技术:(适用于大小型的项目)

requests库

安装

requests用来发送http请求以及接受http响应的python第三方库,主要用于接口自动化测试


pip install requests

requests库常用的方法

requests.get() url是接口的地址,param用于传参

requests.post() url是接口地址,data用于传参,json也适用传参

​ data和json传参的区别:主要是通过请求头Content-Type来区分

​ Content-Type:作用是服务器要求传入的报文的内容类型

​ 请求:请求方式,请求路径,请求头,请求正文

requests.pull()

requests.delete()

requests.request() 可以发送所有类型的请求:get,post,put,delete

拓展:postman的四种传参的方式对应的Content-Type的值如下:

from-data:Content-Type:multipart/form-data、boundary=

x-www-from-urlencoded:Content-Type:applic.ation/x-www-form-urlencoded

raw:

​ text:Content-Type:text/plain

​ javascript:Content-Type:application/javascript

​ json:Content-Type:application/json

​ html:Content-Type:application/xml

binary:Content-Type:application/binary

data和json传参以及Content-Type的关系如下:

1.data传参:

​ data传参:报文时dict类型,那么默认Content-Type application/x-www-form-urlencoded

​ data传参:报文是str类型,那么默认Content-Type:text/plain

2.json传参:

​ 报文可以是dict类型,那么默认Content-Type application/json

注意:

json是字典类型的字符串显示。

json.loads() 把json字符串转化为dict格式,也可以传str格式(如果是嵌套字典那么就需要使用json.dumps()把嵌套字典转化为json字符串)

json.dumps() 把dict格式转换为json字符串(包括嵌套的dict)

不管是get,post,put,delete,都是调用的requests.request方法。而requests.reques方法调用的是session.reques方法

method 请求方式

url 请求路径

params=None get方式传参

data=None post方式传参

json=None post方式传参

headers=None 请求头

cookie=None 请求cookie

files=None 文件上传

requests模块返回的response对象详解

res.json() 获取返回的字典格式的数据res.text 获得返回的字符串格式的数据res.content 获得返回的bytes字节类型的数据res_status_code 返回状态码res.reason 返回状态信息res.cookies 返回cookie信息res.headers 返回响应头res.request.xxx 返回需求的数据,如:需求头,请求参数

请求必须带有请求头的接口,以及需要cookie鉴权和session鉴权的接口


<input type="hidden" name="csrf_token" value="e7202bf558a7eb34"/>

90%以上的基于web的接口都有cookie鉴权

两种解决方式:

使用cookie关联

案例注释:接口关联变量,在第一个接口取值,在第二个接口传值,第三个接口文件上传,访问首页的接口,解决鉴权


import requests


class TestRequest:
    #全局变量,类变量,通过类名调用
    access_token=""
    csrf_token=""
    php_cookie=""

    def test_get_token(self):
        url:"https://api.weixin.qq.com/cgi-bin/token"
        data:{
            "grant_type":"client_credential",
            "appid":"wx74a8627810cfa300",
            "secret":"e40a02f9cf9d79df497e6aaf93ab80"
        }
        res=requests.request(method="get",url=url,params=data)
        print(res.json())
        TestRequest access_token =  res.json()['access_token']

    # post请求:编辑标签接口
    def test_edit_flag(self):
        url:"https://api.weixin.qq.com/cgi-bin/tags/updat?access token="+TestRequest.access_token
        data=[
            "tag":
               {
                   "id":134,
                    "name":"广东人"
                }
        ]
        str_data=json.dumps(data)
         res=requests.request(method="post",url=url,data=str_data)
        print(res.json())


#         文件上传
def test_file_upload(self):
    url:"https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token="+TestRequest.access_token"+TestRequest.access_token
    data={
        "media":open(r"E:shu.png","rb")
    }
    res=requests.request(method="post",url=url,files=data)
    print(res.json())


# 访问首页的接口
def test_start(self):
    url:"https://47.107.116.139/phpwind"
    res=requests.request(method="get",url=url)
    print(res.text)
# 正则提取token
obj=re.search('name="csrf_token" value="(.*?)"',res.text)
# print(obj.group(1))
TestRequest.csrf_token=obj.group(1)
# 解决鉴权的方式一:提取cookie
TestRequest.php_cookie=res.cookies

#     登录接口
def test_login(self):
    url:"https://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
    data={
        "username":"msxy",
        "password":"msxy",
        "csrf_token":TestRequest.csrf_token,
        "backurl":"http://47.107.116.139/phpwind/",
        "invite":""
    }
    # 请求头
    headers={
        "Accept":"application/json, text/javascript, /; q=0.01",
        "X-Requested-with":"XMLHttpRequest"
    }
    res=requests.request(method="post",url=url,data=data,headers=headers,cookies=TestRequest.php_cookie)
    print(res.json())
# 解决鉴权的方式一:提取cookie
TestRequest.php_cookie=res.cookies


if __name__ == '__main__':
    TestRequest().test_get_token()
    TestRequest().test_edit_flag()
    TestRequest().test_file_upload()
    TestRequest().test_start()
    TestRequest().test_login()

使用session关联


import requests


class TestRequest:
    #全局变量,类变量,通过类名调用
    access_token=""
    csrf_token=""
    # php_cookie=""
    sess=requests.session()

    def test_get_token(self):
        url:"https://api.weixin.qq.com/cgi-bin/token"
        data:{
            "grant_type":"client_credential",
            "appid":"wx74a8627810cfa300",
            "secret":"e40a02f9cf9d79df497e6aaf93ab80"
        }
        res=requests.request(method="get",url=url,params=data)
        print(res.json())
        TestRequest access_token =  res.json()['access_token']

    # post请求:编辑标签接口
    def test_edit_flag(self):
        url:"https://api.weixin.qq.com/cgi-bin/tags/updat?access token="+TestRequest.access_token
        data=[
            "tag":
               {
                   "id":134,
                    "name":"广东人"
                }
        ]
        str_data=json.dumps(data)
        res=requests.request(method="post",url=url,data=str_data)
        print(res.json())


#         文件上传
def test_file_upload(self):
    url:"https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token="+TestRequest.access_token"+TestRequest.access_token
    data={
        "media":open(r"E:shu.png","rb")
    }
    res=requests.request(method="post",url=url,files=data)
    print(res.json())


# 访问首页的接口
def test_start(self):
    url:"https://47.107.116.139/phpwind"
    res=TestRequest.sess.request(method="get",url=url)

    # print(res.text)
# 正则提取token
obj=re.search('name="csrf_token" value="(.*?)"',res.text)
# print(obj.group(1))
TestRequest.csrf_token=obj.group(1)

#     登录接口
def test_login(self):
    url:"https://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
    data={
        "username":"msxy",
        "password":"msxy",
        "csrf_token":TestRequest.csrf_token,
        "backurl":"http://47.107.116.139/phpwind/",
        "invite":""
    }
    # 请求头
    headers={
        "Accept":"application/json, text/javascript, /; q=0.01",
        "X-Requested-with":"XMLHttpRequest"
    }
    res=TestRequest.sess.request(method="post",url=url,data=data,headers=headers)
    print(res.json())



if __name__ == '__main__':
    TestRequest().test_get_token()
    TestRequest().test_edit_flag()
    TestRequest().test_file_upload()
    TestRequest().test_start()
    TestRequest().test_login()

pytest接口自动化-进阶篇

目标:python+requests+pytest+allure+jenkins

接口自动化测试框架关于接口关联的封装

策略:去掉全局变量,用yaml文件代替保存下载pyyaml的包(搜搜)


import os
import yaml



# 读取
def read_yaml():
    with open(os.getcwd()+'/extract.yaml',mode='r',encoding='utf-8') 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:
        yaml.dump(data, stream=f,allow_unicode=True)

# 清空
def clear_yaml(data):
    with open(os.getcwd()+'/extract.yaml', mode='w',encoding='utf-8') as f:
        f.truncate()

关于接口自动化框架之统一请求封装

如果两个测试用例要使用同一个session怎么办

通过同一个session发送请求

方便统计用例。日志封装


import requests
# 关于接口自动化框架之统一请求封装
class RequestUtil:
    # 全局变量 类变量 通过类名调用
    sess=requests.session()

    def send_request(self, url, method, datas=None, **kwargs):
        method=str(method).lower()
        res=None
        if method=='get':
            res=RequestUtil.sess.request(method=method,url=url,params=datas,**kwargs)
        elif method=='post':
            if datas and isinstance(datas,dict):
                datas=json.dumps(datas)

            res=RequestUtil.sess.request(method=method,url=url,datas=datas,**kwargs)
        else:
            pass
        return  res

P18接口自动化框架封装之Yaml数据驱动封装

步骤

1.将接口的数据的格式按照这种方式书写:


-
  name:
    request:
      method: get
      url: https://api.weixin.qq.com/cgi-bin/token
      data:
        grant_type: client_crediential
        appid: wx6b11b3efd1cdc290
        secret: 106a9c61571dfdf55f6255cdgs5622
  validate: None

2.在yaml_util新增一个读取测试用例的方法


# 读取测试用例的方法
def read_testcase(yaml_name):
    with open(os.getcwd()+'/testcases/'+yaml_case,mode='r',encoding='utf-8') as f:
        value = yaml.load(stream=f, Loader=yaml.FullLoader)
        return value

3.在test_api.py 通过装饰器读取字典

如:


@pytest.mark.smoke
@pytest.mark.parametrize("args_name",read_testcase('get_token.yaml'))
def test_get_token(self,args_name):
	url=args_name['request']['url']
	data=args_name['request']['data']
	method=args_name['request']['method']
	res=RequestUtil().send_request(method=method,url=url,datas=data )
  • 全部评论(0)
手机二维码手机访问领取大礼包
返回顶部