目录
1、使用setup/teardown/setup_class/teardown_class
2、conftest.py中使用fixture实现部分前后置
单元测试框架
单元测试框架是在自动化测试或者白盒测试中对软件的最小单元(函数,方法)进行测试的框架。
分为Python(unittest、pytest)和Java(Junit、Testing)
主要用来发现测试用例、执行测试用例、判断测试结果、生成测试报告
pytest是一个成熟、灵活且简单的单元测试框架。
可以结合selenium,requests,appium完成各种不同的自动化
一、安装插件requirements.txt
"""
在项目的根目录创建requirements.txt保存插件
再通过命令安装pip install --target=安装文件路径 -r requirements.txt
"""
# 生成html报告
pytest-html
# 改变用例的执行顺序
pytest-ordering
# 失败用例重新执行
pytest-rerunfailures
# 多线程运行
pytest-xdist
# 环境变量
pytest-base-url
# 生成allure报告
allure-pytest
pytest
requests
pyyaml
二、配置文件pytest.ini
[pytest]
# -v(输出更加详细的运行信息)
# -s(输出调试信息)
# -n=num(多线程运行)
# --reruns=num(失败用例重跑)
# -x(只要有一个失败用例就停止运行)
# --maxfail=num(只要有num个用例失败就停止运行)
# --html=报告的路径(生成html报告)
# -m "smoke"(只执行标记用例@pytest.mark.smoke)
addopts = -vs
# 测试用例所在目录
testpaths = ./testcases
# 模块名必须以test_开头
python_files = test_*.py
# 测试类名必须以Test开头
python_classes = Test*
# 测试方法必须以test_开头
python_functions = test_*
# base_url就是一个function级别的手动调用的fixture
base_url = http://101.34.221.219:8010/api.php
# 用例分组
markers =
smoke: 冒烟测试
product_manage: 商品管理
三、前后置
1、使用setup/teardown/setup_class/teardown_class
class CommonUtil:
def setup(self):
print("每个用例之前执行一次")
def teardown(self):
print("每个用例之后执行一次")
def setup_class(self):
print("每个类之前执行一次")
def teardown_class(self):
print("每个类之后执行一次")
2、conftest.py中使用fixture实现部分前后置
"""
@pytest.fixture(scope="function",autouse=False,params=[1,2,3],ids=None,name=None)
scope:作用域
session
package
module
class
funton
autouse:是否自动执行
True
如果测试用例想要调用固件返回值,只需在测试用例参数中添加固件名称
False
针对class,在类头部加上装饰器@pytest.mark.usefixtures('固件名称')
针对function,在测试用例中加入固件名称
params:实现参数化
固件参数里request指向params
固件体里request.param引用
ids:不能单独使用,必须和params一起使用,作用是对参数起别名
name:作用是给固件起别名,原固件名失效
"""
import pytest
@pytest.fixture(scope="session",autouse=True)
def all():
print("在整个项目会话之前")
yield
print("在整个项目会话之后")
四、测试用例
跳过测试用例
@pytest.mark.skip(reason="无理由跳过")
@pytest.mark.skipif(条件,reason="条件成立时跳过")
改变测试用例执行顺序
@pytest.mark.run(order=XX)
断言
assert 1==1
五、数据驱动封装
import allure
import pytest
from common import yaml_util
class TestLogin:
@pytest.mark.parametrize("caseinfo", yaml_util.YamlUtil().read_yaml("./testcases/user_manage/test.yaml"))
def test_login(self, caseinfo, base_url):
print(caseinfo)
import yaml
class YamlUtil:
def read_yaml(self, path):
with open(path, mode="rt", encoding="utf-8") as f:
value = yaml.load(f, yaml.FullLoader)
return value
六、YAML详解
-
story: 登录接口
title: 正例:验证登录成功测试用例
request:
method: post
url: ?s=user/login
headers:
Content-Type: application/json
params:
application: app
application_client_type: h5
data:
accounts: "cang"
pwd: "123456"
type: "username"
validate: null
-
story: 登录接口
title: 反例:验证用户名错误
request:
method: post
url: ?s=user/login
headers:
Content-Type: application/json
params:
application: app
application_client_type: h5
data:
accounts: "c"
pwd: "123456"
type: "username"
validate: null
1、定义
yaml是一种数据类型,扩展名为.yaml或.yml
2、作用
配置一些信息,例如环境变量、数据库信息、用户名密码、日志格式等
3、语法规则
区分大小写
通过缩进表示层级关系,一般用空格,不要使用tab键
通过#注释
字符串可以不用写引号,也可以写单引号或双引号。单引号和双引号的区别在于:单引号会对特殊字符进行转义
4、常用数据类型
标量:整数、浮点数、字符串、布尔、Null、日期
对象:键:(空格)值
数组:通过在一组数据之前加"-"
5、数据类型强转(强转并不是在任何时候都成立)
!!str !!int !!float !!bool !!null !!timestamp !!set !!map !!binary
6、变量的引用
&表示建立一个锚点
*用来引用锚点
<<表示合并
date1: &dates
name: "Cang"
date2:
name1: "百里"
name2: "北凡"
<<: *dates
七、allure报告
1、生成原生allure报告
(1)安装allure-pytest插件
(2)Releases · allure-framework/allure2 · GitHub下载allure,配置环境变量(把allure的bin目录配置到path中)
注意版本兼容:python版本3.10、allure版本2.13、allure-pytest版本2.10、pytest版本7.1
(3)验证是否安装成功
在doc窗口写入:allure --version
(4)生成临时的json报告
在pytest.ini中写入:addopts = -vs --alluredir=./temps --clean-alluredir
(5)生成allure报告
在run.py中写入:os.system("allure generate ./temps -o ./reports --clean")
2、定制企业级allure报告
(1)定制logo
修改D:\allure-2.18.1\config\allure.yaml,增加自定义logo插件:
plugins:
- junit-xml-plugin
- xunit-xml-plugin
- trx-plugin
- behaviors-plugin
- packages-plugin
- screen-diff-plugin
- xctest-plugin
- jira-plugin
- xray-plugin
- custom-logo-plugin
更改D:\allure-2.18.1\plugins\custom-logo-plugin\static\styles.css
.side-nav__brand{
background: url('logo.png') no-repeat left center !important;
margin-left: 10px;
height: 100px;
background-size: contain !important;
}
.side-nav__brand-text{
display: none;
}
(2)定制allure报告功能页面
import allure
import pytest
@allure.epic("项目名称")
@allure.feature("模块名称")
class TestLogin:
@allure.story("接口名称")
@allure.title("用例名称")
@allure.description("用例描述")
@allure.severity(allure.severity_level.TRIVIAL) # 致命BLOCKER,严重CRITICAL,一般NORMAL,提示MINOR,轻微TRIVIAL
# @allure.link("访问接口的链接")
# @allure.issue("bug链接")
# @allure.testcase("测试用例的链接")
def test_login(self):
# 增加用例步骤
with allure.step("用例步骤第一步:输入用户名"):
pass
# 增加用例附件
with open(r"D:\shu.jpg", mode="rb") as f:
content = f.read()
allure.attach(body=content, name="错误截图", attachment_type=allure.attachment_type.JPG)
allure.attach(body="http://www.baidu.com", name="接口请求地址", attachment_type=allure.attachment_type.TEXT)
(3)局域网里访问
在终端中输入:allure open D:\PycharmProject\api_frame\reports
八、requests库详解
1、发送http请求,接收http响应的库
pip install requests
2、常用方法
def get(url, params=None, **kwargs):
url: 接口请求地址
params: 自动以?的方式加到url之后,多个参数用&分割
def post(url, data=None, json=None, **kwargs):
data: 用于传简单的键值对,或者字符串
json: 用于传简单和复杂的键值对
files: 用于传文件
application/x-www-form-urlencoded: 表单 data
multipart/form-data: 带有文件上传的表单 files
application/json: JSON数据格式 json
text/plain、text/xml: 纯文本格式或者xml格式 data
def put(url, data=None, **kwargs):
def delete(url, **kwargs):
-----------------------------------------------------------------------------------
def request(method, url, **kwargs): 是get,post,put,delete的底层方法
-----------------------------------------------------------------------------------
requests.session().request: 是上面request的底层方法
def request(
self,
method, 请求方式
url, 请求路径
params=None, params参数
data=None, data参数
json=None, json参数
files=None, 文件上传
headers=None, 请求头
cookies=None, cookie信息
--------------------------
auth=None, 鉴权
timeout=None, 超时处理
allow_redirects=True, 重定向
proxies=None, 代理
hooks=None, 钩子
stream=None, 文件下载
verify=None, 证书验证
):
3、request的response对象
rep=requests.request()
rep.txt返回字符串的数据
rep.content返回字节格式的数据
rep.json()返回json格式的数据
rep.status_code返回状态码
rep.reason返回状态信息
rep.cookies返回cookie信息
rep.encoding返回编码信息
rep.headers返回响应头信息
rep.request.xxxxxx返回请求的信息
九、提取响应值
1、正则提取
# re.search只匹配一个值,通过下标[1]取值,没有匹配到返回None
token = re.search('"token":"(.*?)",',res.text)[1]
print("token1:%s"%token)
# re.findall匹配多个值,返回列表list,没有匹配到返回None
token = re.findall('"token":"(.*?)",',res.text)
print("token2:%s"%token)
2、jsonpath提取
# jsonpath.jsonpath返回的是一个列表,没有找到返回None
token = jsonpath.jsonpath(res.json(), '$.data.token')
print(token)
3、字典取值