目录:
- pytest参数化用例
- pytest标记测试用例
- pytest设置跳过、预期失败用例
- pytest运行用例
- pytest测试用例调度与运行
- pytest命令行常用参数
- python执行pytest
- pytest异常处理
1.pytest参数化用例
参数化
- 通过参数的方式传递数据,从而实现数据和脚本分离。
- 并且可以实现用例的重复生成与执行。
参数化应用场景
- 测试登录场景
- 测试登录成功,登录失败(账号错误,密码错误)
- 创建多种账号: 中⽂文账号,英⽂文账号
- 普通测试用例方法
- Copy 多份代码 or 读⼊入参数?
- 一次性执⾏多个输⼊入参数
def test_param_login_ok():
# 登录成功
username = "right"
password = "right"
login(username,password)
def test_param_login_fail():
# 登录失败
username = "wrong"
password = "wrong"
login(username,password)
参数化实现方案
- pytest 参数化实现方法
- 装饰器:
@pytest.mark.parametrize
@pytest.mark.parametrize("username,password", [["right", "right"], ["wrong", "wrong"]])
def test_param(username, password):
login(username, password)
Mark:参数化测试函数使用
- 单参数
- 多参数
- 用例重命名
- 笛卡尔积
参数化:单参数情况
- 单参数,可以将数据放在列表中
search_list = ['appium', 'selenium', 'pytest']
@pytest.mark.parametrize('name', search_list)
def test_search(name):
assert name in search_list
示例:
import pytest
search_list = ['appium', 'selenium', 'pytest']
@pytest.mark.parametrize("search_key", ['appium', 'selenium', 'pytest', "", 'requests', 'abc'])
def test_search_param(search_key):
assert search_key in search_list
参数化:多参数情况
- 将数据放在列表嵌套元组中
- 将数据放在列表嵌套列表中
import pytest
# 数据放在元组中
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+5", 7), ("7+5", 12)])
def test_mark_more(test_input, expected):
assert eval(test_input) == expected
# 数据放在列表中
@pytest.mark.parametrize("test_input,expected", [["3+5", 8], ["2+5", 7], ["7+5", 12]])
def test_mark_more(test_input, expected):
assert eval(test_input) == expected
参数化:用例重命名-添加 ids 参数
- 通过ids参数,将别名放在列表中
import pytest
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+5", 7), ("7+5", 12)],
ids=['add_3+5=8', 'add_2+5=7', 'add_3+5=12'])
def test_mark_more(test_input, expected):
assert eval(test_input) == expected
参数化:用例重命名-添加 ids 参数(中文)
# 创建conftest.py 文件 ,将下面内容添加进去,运行脚本
def pytest_collection_modifyitems(items):
"""
测试用例收集完成时,将收集到的用例名name和用例标识nodeid的中文信息显示在控制台上
"""
for i in items:
i.name=i.name.encode("utf-8").decode("unicode_escape")
i._nodeid=i.nodeid.encode("utf-8").decode("unicode_escape")
@pytest.mark.parametrize("test_input,expected",[
("3+5",8),("2+5",7),("7+5",12)
],ids=["3和5相加","2和5相加","7和5相加"])
def test_mark_more(test_input,expected):
assert eval(test_input) == expected
参数化:笛卡尔积
- 两组数据
- a=[1,2,3]
- b=[a,b,c]
- 对应有几种组合形势 ?
- (1,a),(1,b),(1,c)
- (2,a),(2,b),(2,c)
- (3,a),(3,b),(3,c)
import pytest
@pytest.mark.parametrize("b", ["a", "b", "c"])
@pytest.mark.parametrize("a", [1, 2, 3])
def test_param1(a, b):
print(f"笛卡积形式的参数化中 a={a} , b={b}")
2.pytest标记测试用例
Mark:标记测试用例
- 场景:只执行符合要求的某一部分用例 可以把一个web项目划分多个模块,然后指定模块名称执行。
- 解决: 在测试用例方法上加 @pytest.mark.标签名
- 执行: -m 执行自定义标记的相关用例
pytest -s test_mark_zi_09.py -m=webtest
pytest -s test_mark_zi_09.py -m apptest
pytest -s test_mark_zi_09.py -m "not ios"
import pytest
def double(a):
return a * 2
@pytest.mark.int
def test_double_int():
print('test double int')
assert 2 == double(1)
@pytest.mark.minus
def test_double_minus():
print('test double minus')
assert -2 == double(-1)
@pytest.mark.float
def test_double_float():
assert 0.2 == double(0.1)
@pytest.mark.float
def test_double2_float():
assert -10.2 == double(-0.1)
@pytest.mark.zero
def test_double_0():
assert 10 == double(0)
@pytest.mark.bignum
def test_double_bignum():
assert 200 == double(100)
@pytest.mark.str
def test_double_str():
assert 'aa' == double('a')
@pytest.mark.str
def test_double_str():
assert 'a$a$' == double('a$')
在终端运行命令:
pytest test_test1.py -v -s -m "str"
上述代码会出现异常,pytest识别不到这些标签,解决办法如下:
在项目根目录新建一个pytest.ini文件
重新运行:
3.pytest设置跳过、预期失败用例
Mark:跳过(Skip)及预期失败(xFail)
- 这是 pytest 的内置标签,可以处理一些特殊的测试用例,不能成功的测试用例
- skip - 始终跳过该测试用例
- skipif - 遇到特定情况跳过该测试用例
- xfail - 遇到特定情况,产生一个“期望失败”输出
Skip 使用场景
- 调试时不想运行这个用例
- 标记无法在某些平台上运行的测试功能
- 在某些版本中执行,其他版本中跳过
- 比如:当前的外部资源不可用时跳过
- 如果测试数据是从数据库中取到的,
- 连接数据库的功能如果返回结果未成功就跳过,因为执行也都报错
- 解决 1:添加装饰器
@pytest.mark.skip
@pytest.mark.skipif
- 解决 2:代码中添加跳过代码
pytest.skip(reason)
import sys
import pytest
@pytest.mark.skip
def test_aaa():
print("没有任何输出")
assert True
@pytest.mark.skip(reason="代码没有实现")
def test_bbb():
assert False
@pytest.mark.skipif(sys.platform == 'darwin', reason="does now run on mac")
def test_case1():
assert True
@pytest.mark.skipif(sys.platform == 'win', reason="dose not run on windows")
def test_case2():
assert True
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher")
def test_case3():
assert True
def check_login():
# return True
return False
def test_ccc():
if not check_login():
pytest.skip("")
print("end")
xfail 使用场景
- 与 skip 类似 ,预期结果为 fail ,标记用例为 fail
- 用法:添加装饰器
@pytest.mark.xfail
-
xfail 是 pytest 中的一个标记,用于标记测试用例,表示该测试用例在某些条件下不会执行,而是被标记为 pass。通常用于在开发阶段快速验证代码的正确性,而在发布版本中禁用这些测试用例,以避免潜在的问题。使用 xfail 标记的测试用例在运行时会被 pytest 忽略,但它们仍然会被计入测试覆盖率。这使得您可以快速查看哪些测试用例未通过,而无需查看未通过的原因。
import pytest
@pytest.mark.xfail
def test_aaa():
print('test_xfail 方法执行')
# assert 1 == 2
assert 2 == 2
xfail = pytest.mark.xfail
@xfail(reason="有bug")
def test_hello():
assert 0
def test_xfail():
print('****开始测试****')
pytest.xfail(reason='该功能尚未完成')
print('测试过程')
assert 1 == 1
4.pytest运行用例
运行多条用例
- 运行 某个/多个 用例包
- 运行 某个/多个 用例模块
- 运行 某个/多个 用例类
- 运行 某个/多个 用例方法
运行多条用例方式
- 执行包下所有的用例:
pytest [包名]
- 执行单独一个 pytest 模块:
pytest 文件名.py
- 运行某个模块里面某个类:
pytest 文件名.py::类名
- 运行某个模块里面某个类里面的方法:
pytest 文件名.py::类名::方法名
运行结果分析
- 常用的:fail/error/pass
- 特殊的结果:warning/deselect
运行结果分析是测试执行过程中非常重要的一个环节,可以帮助开发人员快速定位和解决问题。常用的结果包括:
- fail:测试用例失败,即预期结果和实际结果不一致。
- error:测试用例执行过程中发生错误,导致测试用例无法继续执行。
- pass:测试用例通过,即预期结果和实际结果一致。
除了常用的结果之外,还有一些特殊的结果,例如:
- warning:测试用例执行过程中出现警告,但测试用例仍然通过。
- deselect:测试用例被标记为跳过,即测试用例被排除在测试执行之外。
5.pytest测试用例调度与运行
命令行参数-使用缓存状态
--lf(--last-failed)
选项会重新运行上一次失败的测试用例。它会跳过所有未失败的测试用例,只运行上一次失败的测试用例。--ff(--failed-first)
选项会先运行上一次失败的测试用例,然后再运行其余的测试用例。它会跳过所有未失败的测试用例,先运行上一次失败的测试用例,然后再运行其余的测试用例。
6.pytest命令行常用参数
--help
-x 用例一旦失败(fail/error),就立刻停止执行
--maxfail=num 用例达到
-m 标记用例
-k 执行包含某个关键字的测试用例
-v 打印详细日志
-s 打印输出日志(一般-vs一块儿使用)
--collect-only(测试平台,pytest 自动导入功能 )
- -x 参数用于设置 pytest 执行行为,一旦有测试用例失败,就会立刻停止执行。这对于调试测试用例非常有用,可以快速定位和修复问题。
- --maxfail=num 参数用于设置 pytest 执行的最大失败次数。当一个测试用例失败时,pytest 会自动跳过所有未失败的测试用例,只运行上一次失败的测试用例。如果一个测试用例执行次数达到 maxfail 次,pytest 就会停止执行。
- -m 参数用于标记测试用例。通过指定 -m 参数,可以过滤掉不符合条件的测试用例,只运行符合条件的测试用例。
- -k 参数用于执行包含某个关键字的测试用例。通过指定 -k 参数,可以过滤掉不符合关键字的测试用例,只运行包含关键字的测试用例。
- -v 参数用于打印详细日志。在执行测试用例时,pytest 会记录每个测试用例的详细信息,包括测试用例的名称、执行时间、返回值等。通过指定 -v 参数,可以查看这些详细信息。
- -s 参数用于打印输出日志。在执行测试用例时,pytest 会记录每个测试用例的输出信息。通过指定 -s 参数,可以查看这些输出信息。
- --collect-only 参数用于设置 pytest 的自动导入功能。在执行 pytest 时,它会自动导入所有测试文件中的模块和类,并生成相应的测试用例。通过指定 --collect-only 参数,可以只执行自动导入功能,而不执行实际的测试用例。
7.python执行pytest
Python 代码执行 pytest
- 使用 main 函数
- 使用 python -m pytest 调用 pytest(jenkins 持续集成用到)
Python 代码执行 pytest - main 函数
if __name__ == '__main__':
# 1、运行当前目录下所有符合规则的用例,包括子目录(test_*.py 和 *_test.py)
pytest.main()
# 2、运行test_mark1.py::test_dkej模块中的某一条用例
pytest.main(['test_mark1.py::test_dkej','-vs'])
# 3、运行某个 标签
pytest.main(['test_mark1.py','-vs','-m','dkej'])
运行方式(在终端输入命令~)
`python test_*.py `
8.pytest异常处理
常用的异常处理方法
- try…except
- pytest.raises()
异常处理方法 try …except
try:
可能产生异常的代码块
except [ (Error1, Error2, ... ) [as e] ]:
处理异常的代码块1
except [ (Error3, Error4, ... ) [as e] ]:
处理异常的代码块2
except [Exception]:
处理其它异常
try:
a = int(input('输入被除数:'))
b = int(input('输入除数:'))
c = a / b
print('您输入的俩个数相除的结果是:', c)
except(ValueError, ArithmeticError):
print('程序发生数字格式异常,算术异常之一')
except:
print('未知异常')
print('程序继续运行')
异常处理方法 pytest.raise()
- 可以捕获特定的异常
- 获取捕获的异常的细节(异常类型,异常信息)
- 发生异常,后面的代码将不会被执行
pytest.raise() 用法
def test_raise():
with pytest.raises(ValueError, match='must be 0 or None'):
raise ValueError("value must be 0 or None")
def test_raise1():
with pytest.raises(ValueError) as exc_info:
raise ValueError("value must be 42")
assert exc_info.type is ValueError
assert exc_info.value.args[0] == "value must be 42"