什么是单元测试框架
单元测试是指在软件开发中,针对软件的最小单位(函数、方法)进行正确性的检查测试
常见的单元测试框架
java:Junit
python: unittest和pytest
单元测试框架主要做什么
- 测试发现:从多个文件里面去找到我们的测试用例
- 测试执行:按照一定的顺序和规则去执行,并生成结果
- 测试判断:通过断言判断预期结果和实际结果的差异
- 测试报告:统计测试进度,耗时,通过率,生成测试报告
单元测试框架和自动化测试框架的关系
自动化测试框架的作用:
- 提高测试效率,降低维护成本
- 减少人工干预,提高测试的准确性,增加代码的重用性
- 核心思想是让不懂代码的人,也能够通过这个框架去实现自动化测试
- 单元测试框架只是自动化测试框架的组成部分之一
自动化测试框架包含以下内容:
- 单元测试框架
- pom设计模式
- 数据驱动
- 关键字驱动
- 全局配置文件的封装
- 日志监控
- selenium,request的二次封装
- 断言
- 报告邮件
- 更多。。。
pytest简介
- 是一个非常成熟的python的单元测试框架,比unittest更灵活,容易上手
- 可以和selenium、requests、appium结合实现web自动化,接口自动化,APP自动化
- 可以实现测试用例的跳过和重试
- 可以和allure生成非常美观的测试报告
- 可以和Jenkins持续集成
- 有很多强大的插件:
使用命令:pip install -r .\pytest\plugiininstall.txt 安装插件
plugiininstall.txt文件
-
pytest
-
pytest-html
-
pytest-xdist
-
pytest-ordering
-
pytest-rerunfailures
-
allure-pytest
- pytest-html(生成HTML格式的自动化测试报告)
- pytest-xdist(测试用例分布式执行,多CPU分发)
- pytest-ordering (用于改变测试用例的执行顺序)
- pytest-rerunfailures(用例失败后重跑)
- allure-pytest(用于生成美观的测试报告)
pytest使用的命名规则
- 模块名必须以test_开头或者_test结尾
- 测试类必须以Test开头,并且不能有init方法
- 测试方法必须以test开头
pytest测试用例的运行方式
- 主函数模式(if __name__ == '__main__':)
- 运行所有:pytest.main()(使用单独的py文件来运行)
- 指定模块:pytest.main(['-vs', 'test_print.py'])(使用单独的py文件来运行)
- 指定目录:pytest.main(['-vs', './interface_testcase'])(使用单独的py文件来运行)
- 通过nodeid指定用例运行:nodeid由模块名,分隔符,类名,方法名,函数名组成。 pytest.main(['-vs', './interface_testcase/test_interface.py::TestInterface::test_01_demo'])(使用单独的py文件来运行)
- 命令行模式
- 运行所有:pytest
- 指定模块:pytest -vs test_print.py
- 指定目录:pytest -vs ./interface_testcase
- 通过nodeid指定用例运行:pytest -vs ./interface_testcase/test_interface.py::TestInterface::test_01_demo
- 通过读取pytest.ini核心配置文件运行
- 位置:项目的跟目录下
- 编码:必须是ANSI,可以使用notepad++改变编码格式
- 作用:改变pytest的默认行为
- 运行的规则:不管是主函数的模式运行,还是命令行的模式运行,都会去读取这个配置文件
-
[pytest]
-
addopts= -vs
-
test_paths =./pytest01
-
python_files=test_*.py
-
python_classes=Testa*
-
python_functions=test
pytest参数详解
- -s: 表示输出调试信息,包括打印信息
- -v: 表示输出更详细信息
- -n: 支持多线程运行测试用例。如:pytest -vs test_print.py -n 2
pytest.main(['-vs', './interface_testcase','-n=2'])
- --reruns:失败用例重跑,--reruns=2
pytest.main(['-vs', './interface_testcase','--reruns=2'])
- -x: 表示只要有一个用例报错,那么测试停止。
- --maxfail=2:出现两个用例失败就停止
- -k: 执行包含指定字符串的测试用例:pytest -vs test_print.py -k "ao"
- --html ./report/report.html:生成报告
- -m:分组执行
pytest执行测试用例的顺序
unittest:是通过ASCII的大小来执行的顺序
pytest:默认是按照从上到下的顺序执行
改变默认的执行顺序:使用mark标记,order代表第几个执行
@pytest.mark.run(order=1)
如何分组执行
分组执行的常用场景:冒烟、分模块执行、分接口和web执行
pytest.ini
-
[pytest]
-
addopts= -vs
-
test_paths =./pytest01
-
python_files=test_*.py
-
python_classes=Testa*
-
python_functions=test
-
markers=
-
smoke:冒烟用例
-
usermanage:用户管理模块
-
productmanage:商品管理模块
在要执行的用例上面标明是哪个分组:
-
@pytest.mark.usermanage
-
def test_01_baili(self):
-
print('测试百里')
-
@pytest.mark.smoke
-
def test_02(self):
-
print('测试2')
-
pytest -vs -m "smoke"
-
pytest -vs -m "smoke or usermanage"
如何跳过执行某个测试用例
-
# 无条件跳过
-
@pytest.mark.skip
-
# 有条件跳过,必须带reason
-
# @pytest.mark.skipif(age == 18, reason='未成年')
-
def test_03(self):
-
print('测试3')
接口自动化
目前主流的接口测试的工具:
postman+newman+git/svn+jenkins
jmeter+ant+git/svn+jenkins
弊端:
- 敏捷开发时代,接口数量巨大,工具无法做到团队协作和版本控制
- 功能写死了,对于一些复杂的接口(自定义加密和签名接口)
- 项目里面有多种协议接口:http、webservice、websocket、dubbo
- 排错,定位问题
- 没有完整的测试报告
- 多接口串联,数据库连接,日志监控
- 有些公司即要做接口自动化,又要做web自动化,接口+web结合
request库
python第三方库,主要用于接口自动化测试
pip install requests
请求方法
请求头:
- Content-Type: 传值的内容格式
- application/x-www-urlencoded:表单
- mutipart/form-data:表单里面有文件上传
- text/plain:文本
- binary:二进制文件
- Accept:客户端接收数据的格式
- X-Requested-With:异步请求
- User-Agent:客户端的用户类型
- Cookie:cookie信息
- get(url, params=None, **kwargs)
- 通过params进行传参
- post(url, data=None, json=None, **kwargs)
- 通过data传参,
- 参数是dict类型:请求头的Content-Type:application/x-www-urlencoded,表示通过表单传参,格式a=1&b=2
- 参数是str类型:Content-Type:text/plain
- 通过json传参,请求头的Content-Type:applicaiton/json,格式{"a":1}
- request(method, url, **kwargs):
- method:请求方法类型
- url:请求地址
- kwargs:字典类型的可变参数
- params=,表示get方法传参
- data=,表示post方法传参
- json=,表示json方法传参
- headers=,表示请求头
- cookies=,表示cookie关联
- files=,表示文件上传
响应
-
res = requests.get(url, params)
-
print(res.json()) # 把返回值转换为一个dict对象
-
print(res.text) # 转换为文本
-
print(res.content) # 转换为字节类型的数据
-
print(res.status_code) # 返回码
-
print(res.reason) # 返回信息
-
print(res.cookies) # cookie信息
-
print(res.encoding) # 编码格式
-
print(res.headers) # 响应头
-
print(res.request) # 请求对象request
json和dict互转
-
json.load() # dict=====>json
-
json.dumps() # json===>dict
pytest.fixture
@pytest.fixture(scope="作用域", params="数据驱动", autouse="自动执行", ids="数据驱动时重命名参数名", name="给fixture作用的函数的重命名")
-
scope参数:
-
function:函数
-
class:类
-
module:模块
-
package/session:会话
作用在方法前后
-
# scope="function":表明作用在方法前后
-
@pytest.fixture(scope="function")
-
def exec_database_sql():
-
print("前置操作-===数据库查询")
-
yield
-
print("后置操作=====数据校验")
-
# 指定要使用的fixture固件:exec_database_sql
-
def test_05(self, exec_database_sql):
-
print('测试5')
-
# 输出为以下内容
-
前置操作-===数据库查询
-
测试5
-
PASSED后置操作=====数据校验
作用在类前后
-
@pytest.fixture(scope="class")
-
def exec_database_sql_class():
-
print("前置操作-===数据库查询")
-
yield
-
print("后置操作=====数据校验")
-
# 指定要使用的固件
-
@pytest.mark.usefixtures('exec_database_sql_class')
-
class TestA:
-
def test_a(self):
-
print('test_a')
-
def test_b(self):
-
print('test_b')
会话的前后置
一般会结合conftest.py文件(用于单独存放fixture固件的配置文件)一起使用。
conftest.py
-
@pytest.fixture(scope="session",autouse=True)
-
def exec_database_sql():
-
print("前置操作-===所有请求前执行一次")
-
yield
-
print("后置操作=====所有请求之后执行一次")
再使用session.request来做请求
common
yaml_util.py
yaml_util提供对yaml文件的操作。可以在需要保存全局变量的地方,将变量写入到yaml文件中
-
import os
-
import yaml
-
# 读yaml文件
-
def read_yaml(key):
-
with open(os.getcwd() + '/extract.yml', encoding='utf-8') as f:
-
yaml_content = yaml.load(stream=f, Loader=yaml.FullLoader)
-
return yaml_content[key]
-
# 写yaml文件,mode='a'表示追加的方式更新
-
def write_yaml(data):
-
with open(os.getcwd() + '/extract.yml', encoding='utf-8', mode='a') as f:
-
yaml.dump(data, stream=f, allow_unicode=True)
-
# 清空yaml文件,mode='w'表示覆盖的方式更新、清空
-
def clear_yaml():
-
with open(os.getcwd() + '/extract.yml', encoding='utf-8', mode='w') as f:
-
f.truncate()
-
conftest.py
-
from common.yaml_util import clear_yaml
-
@pytest.fixture(scope="session",autouse=True)
-
def exec_database_sql():
-
# 每次请求前清空yaml变量文件
-
clear_yaml()
-
print("前置操作-===所有请求前执行一次")
-
yield
-
print("后置操作=====所有请求之后执行一次")
allure测试报告
- 官网下载:https://github.com/allure-framework/allure2/releases
- 安装目录/bin配置到环境变量path中
- 查看是否安装成功:allure --version
- 命令中加入:--alluredir ./temps --clean-aullredir,生成临时的json格式报告到temps的目录下
- 生成HTML报告:os.system("allure generate ./temp -o ./report --clean");注意如果出现乱码错误,可能是由于pyCharm的环境变量没有配置的原因
一个接口对应多个测试用例
- 使用@pytest.mark.parametrize(argnames=,argvalues=),argvalues有多少条值,就有多少个测试用例
-
@pytest.mark.parametrize(argnames='parminfo', argvalues=['啊', '们'])
-
def test_01(self, parminfo):
-
print(parminfo)
-
@pytest.mark.parametrize(argnames='parminfo', argvalues=[['啊', '们'], ['额', '喔']])
-
def test_02(self, parminfo):
-
print(parminfo)
-
@pytest.mark.parametrize(argnames='p1,p2', argvalues=[['啊', '们'], ['额', '喔']])
-
def test_03(self, p1, p2):
-
print(p1, p2)
- 通过yaml来管理接口自动化测试的用例
yaml是一种数据格式,主要用于配置文件或编写用例。yaml中列表用'-'表示
一个py文件可以有多个接口,一般一个接口对应一个yaml文件,yaml文件中有几十个用例,有正例和反例
感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取