本文为霍格沃兹测试开发学社学员学习笔记分享
一、pytest结合数据驱动-yaml
1、什么是数据驱动?
数据驱动就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变。简单来说,就是参数化的应用。数据量小的测试用例可以使用代码的参数化来实现数据驱动,数据量大的情况下建议大家使用一种结构化的文件(例如 yaml,json 等)来对数据进行存储,然后在测试用例中读取这些数据
2、yaml 文件介绍
1)对象:键值对的集合,用冒号 “:” 表示,在python中为字典
2)数组:一组按次序排列的值,前加 “-”,在python中为列表
3)纯量:单个的、不可再分的值,包括字符串、布尔值、整数、浮点数、Null、时间、日期
# {"languages":["PHP","Java","Python"], "book":{"Python入门":{"price": 55.5, "author": "Lily", "available": True, "repertory": 20, "date": "2018-02-17"}, "Java入门": {"price": 60, "author": "Lily", "available": "False", "repertory": "Null", "date": "2018-05-11"}}}
languages:
- PHP
- Java
- Python
book:
Python入门: # 书籍名称
price: 55.5
author: Lily
available: True
repertory: 20
date: 2018-02-17
Java入门:
price: 60
author: Lily
available: False
repertory: Null
date: 2018-05-11
3、查看 yaml 文件
1)pycharm
2)txt 记事本
4、读取 yaml 文件
1)工程目录结构
①data 目录:存放 yaml 测试数据文件data.yaml
②func 目录:存放被测函数文件operation.py
③testcase 目录:存放测试用例文件test_add.py
2)安装:pip install pyyaml
3)方法:yaml.safe_load(f)
# yaml测试数据文件内容
# [[1,1,2],[3,6,9],[100,200,300]]
-
- 1
- 1
- 2
-
- 3
- 6
- 9
-
- 100
- 200
- 300
# operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
result = x + y
return result
# test_add.py 文件内容
import pytest
import yaml
from testing.func.operation import my_add
# 读取yaml文件
def get_data():
"""
获取json数据
:return: 返回数据的结构:[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
"""
# 如果yaml文件中有中文,必须要加上encoding="utf-8"
with open('../data/data.yaml', 'r', encoding="utf-8") as f:
data = yaml.safe_load(f)
return data
class TestWithYAML:
@pytest.mark.parametrize('x,y,expected', get_data()) # 测试数据调用get_data()方法读取出来的数据,需要添加数据时只需在yaml文件即可
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
二、pytest结合数据驱动-excel
1、安装:pip install openpyxl
2、工程目录结构
①data 目录:存放 excel 测试数据文件params.xlsx
②func 目录:存放被测函数文件operation.py
③testcase 目录:存放测试用例文件test_excel.py
# operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
result = x + y
return result
# test_excel.py 测试用例文件内容
import openpyxl
import pytest
from testing.func.operation import my_add
def get_excel():
# 获取工作簿
book = openpyxl.load_workbook('../data/params.xlsx')
# 获取活动行(非空白的)
sheet = book.active
# 提取数据,格式:[[1, 2, 3], [3, 6, 9], [100, 200, 300]]
values = []
for row in sheet:
line = []
for cell in row:
line.append(cell.value)
values.append(line)
return values
class TestWithEXCEL:
@pytest.mark.parametrize('x,y,expected', get_excel())
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
三、pytest结合数据驱动-csv
1、csv文件介绍
1)csv:逗号分隔值,是 Comma-Separated Values 的缩写
2)以纯文本形式存储数字和文本
3)文件由任意数目的记录组成
4)每行记录由多个字段组成
2、csv文件的使用
1)读取数据
①内置函数:open()
②内置模块:csv
③方法:csv.reader(iterable)
参数:iterable ,文件或列表对象
返回:迭代器,每次迭代会返回一行数据。
2)工程目录结构
①data 目录:存放csv 测试数据文件demo.csv
②func 目录:存放被测函数文件operation.py
③testcase 目录:存放测试用例文件test_csv.py
# csv文件内容
1,1,2
3,6,9
100,200,300
# operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
result = x + y
return result
# 测试用例文件内容
import pytest
import csv
from testing.func.operation import my_add
# 读取csv文件内容
def get_csv():
"""
获取csv数据
:return: 返回数据的结构:[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
"""
with open('../data/demo.csv', 'r') as file:
raw = csv.reader(file)
data = []
for line in raw:
data.append(line)
return data
class TestWithCSV:
@pytest.mark.parametrize('x,y,expected', get_csv())
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
四、Pytest 结合数据驱动 json
1、json文件介绍
①json是JS对象,全是JavaScript Object Notation
②是一种轻量级的数据交换格式;json 结构
对象 {"key": value}
数组 [value1, value2 ...]
2、查看 json 文件
①pycharm
②txt 记事本
3、工程目录结构
①data 目录:存放json数据文件params.xlsx
②func 目录:存放被测函数文件operation.py
③testcase 目录:存放测试用例文件test_json.py
4、读取 json 文件
①内置函数 open()
②内置库 json
③方法:json.loads()
# json数据文件
{
"case1": [1, 1, 2],
"case2": [3, 6, 9],
"case3": [100, 200, 300]
}
# operation.py 文件内容
# 被测方法,相加功能
def my_add(x, y):
result = x + y
return result
# 测试用例文件内容
import pytest
import json
from testing.func.operation import my_add
# 读取json文件
def get_json():
"""
获取json数据
:return: 返回数据的结构:[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
"""
with open('../data/param.json', 'r') as f:
data = json.loads(f.read())
return list(data.values())
class TestWithJSON:
@pytest.mark.parametrize('x,y,expected', [[1, 1, 2]])
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
五、pytest测试用例生命周期管理
1、Fixture 用法
1)Fixture 特点及优势
①命令灵活:对于 setup,teardown,可以不起这两个名字
②数据共享:在 conftest.py 配置⾥写⽅法可以实现数据共享,不需要 import 导⼊。可以跨⽂件共享
③scope 的层次及神奇的 yield 组合相当于各种 setup 和 teardown
④实现参数化
2)Fixture 在自动化中的应用- 基本用法
①使用场景:测试⽤例执⾏时,有的⽤例需要登录才能执⾏,有些⽤例不需要登录。
②setup 和 teardown ⽆法满⾜。fixture 可以。默认 scope(范围)function
③步骤:
a.导⼊ pytest
b.在登录的函数上⾯加@pytest.fixture()
c.在要使⽤的测试⽅法中传⼊(登录函数名称),就先登录
d.不传⼊的就不登录直接执⾏测试⽅法。
import pytest
# 定义了登录的fixture,尽量避免以test开头
@pytest.fixture()
def login():
print("完成登录操作")
def test_search():
print("搜索")
def test_cart(login): # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
print("购物车")
def test_order(login): # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
print("下单功能")
2、Fixture 在自动化中的应用 - 作用域
【取值:范围:说明】
①function:函数级:每一个函数或方法都会调用
②class:类级别:每个测试类只运行一次
③module:模块级:每一个.py 文件调用一次
④package:包级:每一个 python 包只调用一次(暂不支持)
⑤session:会话级:每次会话只需要运行一次,会话内所有方法及类,模块都共享这个方法
# fixture的作用域
import pytest
@pytest.fixture(scope="class")
def login1():
print("完成登录操作")
def test_search1():
print("搜索")
def test_cart1(login1): # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
print("购物车")
def test_order1(login1): # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
print("下单功能")
class TestDemo:
def test_case1(self, login1):
print("case1")
def test_case2(self, login1):
print("case2")
3、Fixture 在自动化中的应用 - yield 关键字
1)场景:测试⽅法后销毁清除数据
2)解决:通过在 fixture 函数中加⼊ yield 关键字,yield 是调⽤第⼀次返回结果,第⼆次执⾏它下⾯的语句返回。
3)步骤:
①在@pytest.fixture(scope=module)。
②在登陆的⽅法中加 yield,之后加销毁清除的步骤
import pytest
@pytest.fixture
def login():
# setup操作
print("完成登录操作")
# yield相当于return,不添加返回默认为None,可以添加返回值,在yield后面添加,如返回token
token = "abc"
username = "hello"
yield token # 前面为setup操作,后面为teardown操作
# teardown操作
print("完成登出操作")
def test_search():
print("搜索")
def test_cart(login): # 传入定义了fixture的方法,不需要加括号,执行用例时就会优先执行登录操作
print(f"token:{login}") # 添加返回值时,token为login,拿到token值
print("购物车")
4、Fixture 在自动化中的应用 - 数据共享
1)场景:与其他测试⼯程师合作⼀起开发时,公共的模块要在不同⽂件中,要在⼤家都访问到的地⽅。
2)解决:使⽤ conftest.py 这个⽂件进⾏数据共享,并且他可以放在不同位置起着不同的范围共享作⽤。
3)前提:conftest ⽂件名是不能换的,放在项⽬下是全局的数据共享的地⽅
4)执⾏:系统执⾏到参数 login 时先从本模块中查找是否有这个名字的变量什么的,之后在 conftest.py 中找是否有。
5)步骤:将登陆模块带@pytest.fixture 写在 conftest.py文件
# conftest.py文件
import pytest
@pytest.fixture(scope="session")
def login():
# setup操作
print("完成登录操作")
# yield相当于return,不添加返回默认为None,可以添加返回值,在yield后面添加,如返回token
token = "abc"
username = "hello"
yield token # 前面为setup操作,后面为teardown操作
# teardown操作
print("完成登出操作")
@pytest.fixture()
def loginDB():
print("连接数据库")
yield
print("断开数据库连接")
# 新建测试用例文件
# 将登录操作放在conftest文件中,且方法需要定义为fixture,实现共享,可以直接调用
def test_cart(login):
print("购物车")
def test_order(login):
print("下单功能")
class TestDemo:
def test_case1(self, login, loginDB): # 可以添加多个fixture
print("case1")
def test_case2(self, login, loginDB):
print("case2")
5、Fixture在自动化中的应用-自动应用
1)场景:
①不想原测试⽅法有任何改动,或全部都⾃动实现⾃动应⽤,
②没特例,也都不需要返回值时可以选择⾃动应⽤
2)解决:使⽤ fixture 中参数 autouse=True 实现
3)步骤:在⽅法上⾯加 @pytest.fixture(autouse=True)
# conftest.py文件
@pytest.fixture(scope="session", autouse=True)
def login():
# setup操作
print("完成登录操作")
# yield相当于return,不添加返回默认为None,可以添加返回值,在yield后面添加,如返回token
token = "abc"
username = "hello"
yield token # 前面为setup操作,后面为teardown操作
# teardown操作
print("完成登出操作")
# 测试用例文件
import pytest
# conftest文件中登录操作,fixture方法添加参数autouse=True,其他文件调用不需添加login方法名称,可自动生效
def test_cart():
print("购物车")
def test_order():
print("下单功能")
6、Fixture在自动化中的应用-参数化
1)场景:测试离不开数据,为了数据灵活,⼀般数据都是通过参数传的
2)解决:fixture 通过固定参数 request 传递
3)步骤:
①在 fixture 中增加@pytest.fixture(params=[1, 2, 3, ‘linda’])
②在⽅法参数写 request,方法体里面使用 request.param 接收参数
import pytest
@pytest.fixture(params=[["hali", 123], ["bob", 123456]])
def login(request):
print(f"用户名和密码是:{request.param}")
return request.param
def test_case(login):
print(f"用户名和密码数据为:{login}"
六、pytest配置文件
1、pytest.ini是什么
①pytest.ini 是 pytest 的配置文件
②可以修改 pytest 的默认行为
③不能使用任何中文符号,包括汉字、空格、引号、冒号等等
2、pytest.ini作用
①修改用例的命名规则
②配置日志格式,比代码配置更方便
③添加标签,防止运行过程报警告错误
④指定执行目录
⑤排除搜索目录
新建一个pytest.ini文件,添加以下目录
[pytest]
;执行check_开头和 test_开头的所有的文件,后面一定要加*
python_files = check_* test_*
;执行所有的以Test和Check开头的类
python_classes = Test* Check*
;执行所有以test_和check_开头的方法
python_functions= test_* check_*
;-vs打印日志文件,--alluredir ./results 生成报告并存放在results中,执行用例时就不用再该命令了
addopts = -vs --alluredir ./results
;设置执行的路径
testpaths = testing testcase
;忽略某些文件夹/目录
norecursedirs = result logs datas test_demo*
3、日志配置:
1)注意:windows系统 需要把中文 注释去掉。
[pytest]
;日志开关 true false
log_cli = true
;日志级别
log_cli_level = info
;打印详细日志,相当于命令行加 -vs
addopts = --capture=no
;日志格式
log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
;日志时间格式
log_cli_date_format = %Y-%m-%d %H:%M:%S
;日志文件位置
log_file = ./log/test.log
;日志文件等级
log_file_level = info
;日志文件格式
log_file_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
;日志文件日期格式
log_file_date_format = %Y-%m-%d %H:%M:%S
七、pytest插件
1、pytest 插件分类
①外部插件:pip install 安装的插件
②本地插件:pytest自动模块发现机制,自己编写的插件(conftest.py 存放的)
③内置插件:代码内部的_pytest 目录加载
2、pytest 常用的插件
(1)pytest-ordering:控制用例的执行顺序
1)场景:对于集成测试,经常会有上下文依赖关系的测试用例。比如 10 个步骤,拆成 10 条 case,这时候能知道到底执行到哪步报错。用例默认执行顺序:自上而下执行
2)解决:可以通过 setup,teardown 和 fixture 来解决。也可以使用对应的插件。
3)安装:pip install pytest-ordering
4)用法:@pytest.mark.run(order=2)
5)注意:多个插件装饰器(>2)的时候,有可能会发生冲突
import pytest
@pytest.mark.run(order=2)
def test_case01():
print("case01")
@pytest.mark.run(order=1)
def test_case02():
print("case02")
(2)pytest-xdist:分布式并发执行测试用例
1)场景:假设有个报名系统,对报名总数统计,数据同时进行修改操作的时候有可能出现问题,需要模拟这个场景,需要多用户并发请求数据。
2)使用分布式并发执行测试用例。分布式插件:pytest-xdist
3)安装及运行: pip install pytest-xdist
4)注意: 用例多的时候效果明显,多进程并发执行,同时支持 allure
5)分布式执行测试用例原则
①用例之间是独立的,不要有依赖关系
②用例执行没有顺序,随机顺序都能正常执行
③每个用例都能重复运行,运行结果不会影响其他用例
6)运行命令
①pytest -n numcpus :numcpus 是指几核
②pytest -n auto :不知道是几核时,auto会自动去找空闲的cpu
(3)pytest-dependency:控制用例的依赖关系
(4)pytest-rerunfailures:失败重跑
(5)pytest-assume:多重较验
(6)pytest-random-order:用例随机执行
(7)pytest-html:测试报告
3、pytest 内置插件 hook 体系
1)pytest hook介绍
①是个函数,在系统消息触发时被系统调用
②自动触发机制
③Hook 函数的名称是确定的
④pytest 有非常多的勾子函数
⑤使用时直接编写函数体