0 自动化测试框架
自动化测试框架中的组成部分:
- 单元测试框架
- pom设计模式
- 数据驱动
- 关键字驱动
- 全局配置文件的封装
- 日志监控
- selenium,requests二次封装
- 断言
- 报告邮件
- ...
单元测试框架
- 单元测试:针对软件的最小单位(函数、方法)进行正确性的检查测试
- 单元测试框架:
- java:junit和testing
- python:unittest和pytest
- 单元测试框架
- 测试发现:从多个py模块文件中找到测试用例
- 测试执行:按照一定的顺序和规则执行
- 测试判断:通过断言判断预期结果和实际结果的差异
- 测试报告:统计测试结果(耗时、通过率等)
1 Pytest简介
1 pytest是一个非常成熟的python的单元框架,比unittest更灵活,容易上手。
2 pytest可以和selenium , requests,appium结合实现web自动化,接口自动化, app自动化。
3 pytest可以实现测试用例的跳过以及reruns失败用例重试。
4 pytest可以和allure生成非常美观的测试报告。
5 pytest可以和Jenkins持续集成。
6 pytest有很多非常强大的插件,并且这些插件能够实现很多的实用的操作。
pytest
pytest-html 生成htm格式的自动化测试报告
pytest-xdist 测试用例分布式执行 多CPU分发
pytest-ordering 用于改变测试用例的执行顺序
pytest-rerunfailures 用例失败后重跑
allure-pytest 用于生成美观的测试报告
使用这些插件时,可以集中放到 requirements.txt 文件中
在通过命令进行一次性安装所有插件 pip install -r requirements.txt
2 Pytest的规则
模块名必须以test_开头或_test结尾
测试类必须以Test开头,并且不能有init方法
测试方法必须以test开头
3 Pytest测试用例的运行方式
默认为串行方式执行每个测试用例
1.主函数模式
(1)运行所有: pytest.main()
(2)指定模块: pytest.main(["-vs","-n","2","test_ login.py"])
(3)指定目录: pytest.main(["-s","--maxfail=2","./Intertace. testcase"])
(4)通过nodeid指定用例运行: nodeid由模块名,分隔符,类名,方法名,函数名组成。
pytest.main("-vs.","./interface_testcase/test_interface.py::test_04_func")
pytest.main("-vs","./interface_testcase/test_interface.py::TestInterface.test 03_ zhiliao")2.命令行模式
(1)运行所有: pytest
(2)指定模块: pytest -vs test_ login.py
(3)指定目录: pytest -vs ./interface_ testcase参数详解:
-s 表示输出调试信息,包括print打印的信息
-v 显示更详细的信息
-vs 这两个参数一起用
-n 支持多线程或者分布式运行测试用例(并行),如 pytest -vs ./test_ login.py -n 2
--reruns NUM 失败用例会跑NUM次
-x 表示只要一个用例报错,那么测试停止
--maxfail NUM 出现NUM个用例失败就停止
-k 根据测试用例的部分字符串指定测试用例
如 pytest -vs ./testcase -k "ao"3.通过pytest.ini配置文件运行
4 Pytest测试用例的执行顺序
Unittest测试用例的执行顺序是按名称ASCII码值大小来执行的
Pytest默认从上到下顺序执行
Pytest改变执行顺序,通过@pytest.mark.run()方法
@pytest.mark.run(order=1)
5 pytest.ini配置文件
pytest.ini配置文件一般放在项目的根目录,编码一般为ANSI(在不同的系统中,ANSI表示不同的编码),pytest.ini配置文件可以改变pytest的默认行为,不管是何种运行方式,都会读取该pytest.ini配置文件。
5.1 改变pytest命名规则
配置项如下(注意实际使用时请去除中文注释)
[pytest]
#命令行参数,用空格分开
addopts = -vs
#测试用例文件夹
testpaths = ./TestCase
#配置测试搜索的模块文件名称
python_files = test*.py
#配置测试搜索的测试类名称
python_classes = Test*
#配置测试搜索的测试方法名
python_functions = test
示例:只执行test_01开头的测试用例方法
结果运行如下,仅执行test_01开头的测试用例方法
5.2 改变pytest执行策略
小知识:python装饰器
实质: 是一个函数
参数:是你要装饰的函数名(并非函数调用)
返回:是装饰完的函数名(也非函数调用)
作用:为已经存在的对象添加额外的功能
特点:不需要对对象做任何的代码上的变动
5.2.1 markers配置项进行分组执行
通过markers配置项,可以进行分组
[pytest]
addopts = -vs
testpaths = ./TestCase
python_files = test*.py
python_classes = Test*
python_functions = test
markers =
smoke:冒烟用例
login:登录管理模块
user:用户管理模块
示例:只执行smoke的用例(冒烟用例)
(1) 首先通过@pytest.mark.smoke装饰冒烟用例方法,其中 smoke 可以是任意值,和 markers 的配置一致即可!
(2) 再执行命令, 通过 -m 参数指定执行的组
pytest -vs ./TestCase -m smoke
其中 -m 参数指定的值支持or and 运算符,如下命令表示执行由smoke或者login修饰的测试用例
pytest -vs ./TestCase -m "smoke or login"
5.2.1 指令跳过
(1) 无条件指令跳过
通过 @pytest.mark.skip(reason="xxx") 装饰器修饰
(2) 条件指令跳过
通过 @pytest.mark.skipif(condition,reason="xxx") 装饰器修饰,例如 @pytest.mark.skipif(age>=18,''已成年")
6 报告生成
使用 pytest-html 插件
使用命令: --html 报告输出位置
示例:
使用allure-pytest插件
注意:除了使用pip或pip3安装了allure-pytest插件外,还需要安装allure命令行工具
allure命令行工具(windows下载zip包即可):Releases · allure-framework/allure2 · GitHub
解压allure命令行工具后,记得配置对应allure的路径bin到系统环境变量里
使用allure步骤如下:
(1)使用 --alluredir 输出文件夹名称
[pytest]
addopts = -vs --alluredir allure
testpaths = ./TestCase
python_files = test*.py
python_classes = Test*
python_functions = test
markers =
smoke:冒烟用例
login:登录管理模块
user:用户管理模块
(2)此时运行后,在当前目录下新增一个allure文件夹,其下生成了很多json文件
(3)将json文件生成报告命令:allure generate ./allure -o ./report --clean
import os
import pytest
if __name__=='__main__':
pytest.main()
os.system('allure generate ./allure -o ./report --clean')
-o 指定输出报告的位置
--clean 表示清空原来报告目录下的文件
(4)点击report目录下的index.html进入,可查看详细报告
7 setup teardown setup_class teardown_class 实现每个/全部用例的前后置方法
setup() 每个测试用例执行之前会执行的方法
teardown() 每个测试用例执行之后会执行的方法
示例
- setup_class()
- 所有测试用例执行之前会执行的方法,比如创建日志对象,创建数据库的连接,创建接口的请求对象
- teardown_class()
- 所有测试用例执行之后会执行的方法,比如销毁日志对象,销毁数据库的连接等
示例
8 fixture装饰器实现`部分`测试用例的前后置
使用@pytest.fixture()装饰器来实现`部分`测试用例的前后置
@pytest.fixture(scope="",params="",autouse="",ids="",name="")
- scope
- 表示的是被@pytest.fixture()装饰器修饰的方法的作用域,值有function(默认)、class、module、package、session
- function ---每一个函数或方法都会调用
- class ---每一个类调用一次,一个类中可以有多个方法
- module ---每一个.py文件调用一次,该文件内又有多个function和class
- session ---是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module
- params
- 参数化
- 支持 列表、元素、字典列表[{},{},{}...]、字典元组({},{},{}...)
- autouse
- 默认为false,值为true表示自动使用,此时所有测试用例都会使用被@pytest.fixture()装饰器修饰的方法
- ids
- 当使用params参数化时,给每一个值设置一个变量名
- name
- 给被@pytest.fixture()装饰器修饰的方法取一个别名
示例1:前后置方法,用 yield ,yield前是前置,yield后是后置
示例2:autouse参数为true,所有测试用例都会使用该fixture装饰器的修饰的方法
示例3:返回值
示例4:
参数化,方法接收params数据,必须要用request
params里面个数为用例执行的次数
示例5:idx设置参数名称
示例6:name设置别名,注意设置了name的值后,原名称不可用
9 conftest.py和@pytest.fixture()结合使用实现`全局`的前后置
conftest.py文件单独存放的一个夹具配置文件,名称必须是conftest
conftest.py提供的fixture函数,可以供当前目录下及子目录下的py文件中使用
示例1:
在conftest.py里定义一个fixture函数
在测试用例方法调用fixture函数
示例2:使用多个conftest.py里的方法,调用时从右到左,如下图所示
先执行all_fixture的前置
再执行login_fixture的前置
再执行login_fixture的后置
再执行all_fixture的后置
10 断言
assert
- assert xx 判断xx为真
- assert not xx 判断xx不为真
- assert a in b 判断b包含a
- assert a == b 判断a等于b
- assert a != b 判断a不等于b
- ...
11 YAML数据驱动的封装
@pytest.mark.parametrize(args_name,args_value)
args_name 参数名
args_value 可以是列表、元组、字典列表、字典元组,接口用例数量和其长度一致
示例1:多个参数
YAML详解
- 表示列表
键(冒号)(空格)只 表示字典
YAML数据类型示例
示例:
目录清单
(1)编写数据(test_data.yml文件)
-
name: liuc01
request:
method: get
url: 127.0.0.1
data:
id: 45673
secret: jadgierg892hgag
validate: None
-
name: liuc02
request:
method: get
url: 127.0.0.1
data:
id: 45673
secret: jadgierg892hgag
validate: None
-
name: liuc03
request:
method: get
url: 127.0.0.1
data:
id: 45673
secret: jadgierg892hgag
validate: None
(2)编写读取yaml数据的方法(yaml_util.py文件)
import os
import yaml
class YamlUtil:
#读取测试用例数据
def read_test_data_yaml(self,yamlname):
with open(os.getcwd()+"/TestData/"+yamlname,mode="r",encoding="utf-8") as f:
value = yaml.load(stream=f,Loader=yaml.FullLoader)
return value
(3)测试用例
import pytest
from common.yaml_util import YamlUtil
class TestLogin:
@pytest.mark.parametrize('testdata',YamlUtil().read_test_data_yaml('test_data.yml'))
def test_01_success(self,testdata):
print("Login01 Success")
print(testdata)
if __name__=='__main__':
pytest.main()
运行结果:
END