WEB UI自动化测试之Pytest框架学习
文章目录
- 前言
- Pytest简介
- Pytest安装
- Pytest的常用插件
- Pytest的命名约束
- Pytest的运行方式
- Pytest运行方式与unittest对比
- 主函数运行
- 命令行运行
- 执行结果代码说明
- pytest.ini配置文件方式运行(推荐)
- 使用markers标记测试用例
- pytest中添加Fixture(测试夹具)
- pytest中的setup与teardown
- conftest
- pytest中的fixtrue装饰器
- pytest中的fixtrue装饰器的优势
- Fixture装饰器的定义方式
- fixtrue装饰器参数详解-scope
- scope = “function”
- scope = “class”
- scope = “module”
- fixtrue参数详解-autouse
- 测试用例添加、加载、运行(对比unittest)
- pytest跳过测试用例skip、skipif
- @pytest.mark.skip
- @pytest.mark.skipif
- 跳过标记
- Pytest参数化
- 函数返回值作为参数
- “笛卡尔积”,多个参数化装饰器
- 输出测试报告
- 预期失败函数
- 作用
- 应用场景
- 语法与参数
- 控制方法执行顺序
- 简介
- 安装 pytest-ordering
- 使用方法
- 示例
- 示例1:基于 order 的执行顺序
- 示例2:基于 before/after 的执行顺序
- 失败重试
- 简介
- 安装
- 使用方法
- 装饰器方式
- 参数添加运行方式
- 参考目录
前言
阅读本文前请注意最后编辑时间,文章内容可能与目前最新的技术发展情况相去甚远。欢迎各位评论与私信,指出错误或是进行交流等。
在之前已经学习过用unittest测试框架,本文介绍另外一个目前主流的测试框架Pytest。
Pytest简介
官方文档介绍:
Pytest is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or library.
官方地址:https://docs.pytest.org
pytest是一个非常成熟的Python测试框架,主要有以下几个特点:
- 简单灵活,容易上手
- 支持参数化
- 能够支持简单的单元测试和复杂的功能测试,还可以配合selenium/appnium等进行自动化测试、接口自动化测试(pytest+requests)
- pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-selenium(集成selenium)、pytest-html(html测试报告生成)、pytest-rerunfailures(失败case重复执行)、pytest-xdist(多CPU分发)等
- 测试用例的skip和xfail处理
- 可以很好的和jenkins集成
- 测试报告框架----allure 也支持pytest
Pytest安装
安装命令
pip install -U pytest
查看是否安装成功
pytest --version # 展示当前已安装版本
Pytest的常用插件
插件列表网址:https://plugincompat.herokuapp.com
包含很多插件包,大家可依据工作的需求选择使用。
Pytest的命名约束
1)模块名(py文件)通常被统一放在一个testcases文件夹中,必须是以test_开头或者_test.py结尾
2)测试类(class)必须以Test开头,并且不能带init方法,类里的方法必须以test_开头
3)测试用例(函数)必须以test_开头
此时,在执行pytest命令时,会自动从当前目录及子目录中寻找符合上述约束的测试函数来执行。
# 测试函数必须以test_开头(类以外)def test_demo1(self):print("测试用例1")# 测试类(class)必须以Test开头
class TestDemo:# 类里的方法必须以test_开头def test_demo2(self):print("测试用例2")
Pytest的运行方式
Pytest运行方式与unittest对比
运行步骤 | Pytest | unittest |
---|---|---|
导包 | 导入pytest模块 | 导入unittest模块 |
创建测试方法(不写在测试类中的) | 测试方法名称必须以test开头 | 测试方法名称必须以test开头 |
创建测试类 | 必须以Test开头,并且不能带init方法 | 测试类的命名不做要求,但需要继承unittest.TestCase类。 |
添加测试固件(fixtrue 非必需) | setUp()、tearDown()系列方法 、 fixtrue装饰器 | 使用setUp()、tearDown()系列方法 |
测试类中定义测试方法(即测试用例) | 测试方法名称必须以test开头 | 测试方法名称必须以test开头 |
执行测试用例 | 通过主函数/命令行/配置文件 方式运行 | 通过主函数 或者 使用TestSuite/TestLoader/TextTestRunner |
主函数运行
import pytest
# 创建测试方法(不写在测试类中的)
def test_01():print("啥也没有")
# 通过主函数方式运行
if __name__=='__main__':pytest.main()
main()中可使用的参数有:
参数 | 描述 | 案例 |
---|---|---|
-v | 输出调试信息。 | 如:打印信息 pytest.main([‘-v’,‘testcase/test_one.py’,‘testcase/test_two.py’]) |
-s | 输出更详细的信息,如:文件名、用例名 | pytest.main([‘-vs’,‘testcase/test_one.py’,‘testcase/test_two.py’]) |
-n | 多线程或分布式运行测试用例 | |
-x | 只要有一个用例执行失败,就停止执行测试 | pytest.main([‘-vsx’,‘testcase/test_one.py’]) |
– maxfail | 出现N个测试用例失败,就停止测试 | pytest.main([‘-vs’,‘-x=2’,‘testcase/test_one.py’] |
–html=report.html | 生成测试报告 | pytest.main([‘-vs’,‘–html=./report.html’,‘testcase/test_one.py’]) |
-m | 通过标记表达式执行 | |
-k | 根据测试用例的部分字符串指定测试用例,可以使用and,or |
命令行运行
文件路径:testcase/test_one.py
def test_a():print("啥也没有")assert 1==1
终端输入:pytest ./testcase/test_one.py --html=./report/report.html
命令行中可使用的参数有:
参数 | 描述 | 案例 |
---|---|---|
-v | 输出调试信息。 | pytest -x ./testcase/test_one.py |
-q | 输出简单信息。 | pyets -q ./testcase/test_one.py |
-s | 输出更详细的信息,如:文件名、用例名 | pytest -s ./testcase/test_one.py |
-n | 多线程或分布式运行测试用例 | |
-x | 只要有一个用例执行失败,就停止执行测试 | pytest -x ./testcase/test_one.py |
– maxfail | 出现N个测试用例失败,就停止测试 | pytest --maxfail=2 ./testcase/test_one.py |
–html=report.html | 生成测试报告 | pytest ./testcase/test_one.py --html=./report/report.html |
-k | 根据测试用例的部分字符串指定测试用例,可以使用and,or |
执行结果代码说明
- Exit code 0 所有用例执行完毕,全部通过
- Exit code 1 所有用例执行完毕,存在Failed的测试用例
- Exit code 2 用户中断了测试的执行
- Exit code 3 测试执行过程发生了内部错误
- Exit code 4 pytest 命令行使用错误
- Exit code 5 未采集到可用测试用例文件
pytest.ini配置文件方式运行(推荐)
不管是mian执行方式还是命令执行,最终都会去读取pytest.ini文件。
配置有 pytest.ini 的工程, 只需要打开命令行输入pytest,即可进行测试。
在项目的根目录下创建pytest.ini文件,包含以下内容
[pytest]
addopts=-vs -m slow --html=./report/report.html
testpaths=./scripts
test_files=test_*.py
test_classes=Test*
test_functions=test_*
makerers=smock:冒烟测试用例regression: 回归测试标记
参数 | 作用 |
---|---|
[pytest] | 用于标志这个文件是pytest的配置文件 |
addopts | 命令行参数,多个参数之间用空格分隔 |
testpaths | pytest要进行测试的工作目录 |
test_files | 工作目录下要进行测试的文件名匹配规则 |
test_classes | 工作目录下 测试文件中 可执行类的名称匹配规则 |
test_functions | 测试类中 测试方法名的匹配规则 |
markers | 用例标记,自定义mark,需要先注册标记 |
使用markers标记测试用例
在 pytest 中,markers 是一种非常有用的功能,它可以对测试用例进行标记,以便实现更灵活的测试执行和分组等操作。
**在pytest.ini文件中自定义 markers。**例如:
makerers=
smock:冒烟测试用例
regression: 回归测试标记
这里创建了冒烟测试和回归测试的标记,我们就可以使用这个标记为测试用例进行标记。如下所示:
import pytest@pytest.mark.smoke
def test_01():assert 1==1@pytest.mark.regression
def test_02():assert 2==2
如果不想每次都输入-m smoke,我们可以把这部分放到pytest.ini下。
pytest中添加Fixture(测试夹具)
pytest中的setup与teardown
Pytest也提供了类似于unittest中 setup、teardown的方法,并且分为了五类:
- 模块级别:setup_module、teardown_module
- 函数级别:setup_function、teardown_function,不在类中的方法
- 类级别:setup_class、teardown_class
- 方法级别:setup_method、teardown_method
- 方法细化级别:setup、teardown
我们直接来看代码和运行结果
#!/usr/bin/env python
# -*- coding: utf-8 -*-import pytestdef setup_module():print("=====整个.py模块开始前只执行一次:打开浏览器=====")def teardown_module():print("=====整个.py模块结束后只执行一次:关闭浏览器=====")def setup_function():print("===每个函数级别用例开始前都执行setup_function===")def teardown_function():print("===每个函数级别用例结束后都执行teardown_function====")def test_one():print("one")def test_two():print("two")class TestCase():def setup_class(self):print("====整个测试类开始前只执行一次setup_class====")def teardown_class(self):print("====整个测试类结束后只执行一次teardown_class====")def setup_method(self):print("==类里面每个用例执行前都会执行setup_method==")def teardown_method(self):print("==类里面每个用例结束后都会执行teardown_method==")def setup(self):print("=类里面每个用例执行前都会执行setup=")def teardown(self):print("=类里面每个用例结束后都会执行teardown=")def test_three(self):print("three")def test_four(self):print("four")if __name__ == '__main__':pytest.main(["-q", "-s", "-ra", "setup_teardown.py"])
注意,从执行结果我们可以看到:
- 模块级别:setup_module、teardown_module 在整个.py模块开始前和结束后只执行一次;
- 函数级别:setup_function、teardown_function,对不在类中的方法生效。在每个函数级别用例开始前和结束后执行;
- 类级别:setup_class、teardown_class,整个测试类开始前和结束后执行一次;
- 方法级别:setup_method、teardown_method,测试类里面每个测试用例开始前和结束后都执行一次;
- 方法细化级别:setup、teardown,跟前面方法级别使用类似,不过setup在setup_method之后执行,teardown在teardown_method之前执行;
Pytest除了与unittest中类似的提供了setup和teardown方法外,还提供了使用装饰器的格式来为测试用例添加Fixture。
conftest
在介绍装饰器格式的Fixture前,先介绍conftest。
Conftest它是pytest的一个组件,用于配置测试环境和参数。
因此,conftest.py 文件是存放测试夹具(Fixtures)的理想场所。
测试夹具就像是测试用例执行时的得力助手,能够为测试提供各种必要的资源,如数据、对象、环境等。
通过在 conftest.py 中定义测试夹具,我们可以在多个测试模块中轻松地共享和复用这些资源,避免了在每个测试文件中重复编写相同的准备代码,大大提高了代码的可维护性和测试效率。
- 运行测试之前,pytest会自动识别并执行conftest.py文件中的配置。
- 运行测试之前,pytest 会默认读取 conftest.py里面的所有 fixture
- conftest.py 文件名称是固定的,不能改动
- conftest.py 只对同一个 package 下的所有测试用例生效
- 不同目录可以有自己的 conftest.py,一个项目中可以有多个 conftest.py
- 测试用例文件中不需要手动 import conftest.py,pytest 会自动查找
** 各级工作目录下的conftest.py**
pytest中的fixtrue装饰器
我们可以在conftest.py中,或者是测试模块中定义fixtrue装饰器。
如果有以下场景:1.用例一需要执行登录操作;2.用例二不需要执行登录操作;3.用例三需要执行登录操作,则unittest框架中的setup和teardown则不满足要求。而Pytest中的fixture装饰器就可以解决这一问题
pytest中的fixtrue装饰器的优势
- 命名方式灵活,不限于setup和teardown两种命名
- conftest.py可以实现数据共享,不需要执行import 就能自动找到fixture装饰器
Fixture装饰器的定义方式
@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None)
fixtrue装饰器参数详解-scope
用于控制Fixture装饰器的作用范围,默认取值为function(函数级别)
取值 | 范围 | 说明 |
---|---|---|
function | 函数级 | 每一个函数或方法都可以调用,在测试用例执行前生效。 |
class | 模块级 | 测试类内的测试用例可以调用,具体在何处生效得视情况而定 |
module | 模块级 | 每一个.py文件调用一次, 在第一次调用fixtrue的地方生效 |
session | 会话级 | 每次会话只运行一次,会话内所有方法及类,模块都共享这个fixtrue |
scope = “function”
- 场景一:做为参数传入
import pytest@pytest.fixture()
def login():print("打开浏览器")a = "account"return a@pytest.fixture()
def logout():print("关闭浏览器")class TestLogin:#传入lonin fixturedef test_001(self, login):print("001传入了loging fixture")assert login == "account"#传入logout fixturedef test_002(self, logout):print("002传入了logout fixture")def test_003(self, login, logout):print("003传入了两个fixture")def test_004(self):print("004未传入仍何fixture哦")if __name__ == '__main__':pytest.main()
从运行结果可以看出,fixture函数做为参数传入时,会先执行所有的fixture函数。
- 场景二:Fixture的相互调用
import pytest@pytest.fixture()
def account():a = "account"print("第一层fixture")return a#Fixture的相互调用一定是要在测试类里调用这层fixture才会生次,普通函数单独调用是不生效的
@pytest.fixture()
def login(account):print("第二层fixture")class TestLogin:def test_1(self, login):print("直接使用第二层fixture,返回值为{}".format(login))def test_2(self, account):print("只调用account fixture,返回值为{}".format(account))if __name__ == '__main__':pytest.main()
1.即使fixture之间支持相互调用,但普通函数直接使用fixture是不支持的,一定是在测试类中的测试函数内调用才会逐级调用生效
2.有多层fixture调用时,最先执行的是最内层fixture,而不是先执行传入测试函数的fixture
3.上层fixture的值不会自动return,这里就类似函数相互调用一样的逻辑
scope = “class”
当fixture的作用范围是class时, 具体如何生效要根据fixture的写法来决定。一共有两种情况
1.当测试类内的每一个测试方法都调用了fixture,fixture只在该class下所有测试用例执行前执行一次
2.测试类下面只有部分测试方法使用了fixture函数名。fixture只在该class下第一个使用fixture函数的测试用例执行前执行一次
- 场景一: 每一个测试方法都调用了fixture
import pytest
# fixture作用域 scope = 'class'
@pytest.fixture(scope='class')
def login():print("scope为class")class TestLogin:def test_1(self, login):print("用例1")def test_2(self, login):print("用例2")def test_3(self, login):print("用例3")if __name__ == '__main__':pytest.main()
- 场景二: 部分测试方法都调用了fixture
import pytest
@pytest.fixture(scope='class')
def login():a = '123'print("输入账号密码登陆")class TestLogin:def test_1(self):print("用例1")def test_2(self, login):print("用例2")def test_3(self, login):print("用例3")def test_4(self):print("用例4")if __name__ == '__main__':pytest.main()
scope = “module”
import pytest@pytest.fixture(scope='module')
def login():print("fixture范围为module")def test_01():print("用例01")def test_02(login):print("用例02")class TestLogin():def test_1(self):print("用例1")def test_2(self):print("用例2")def test_3(self):print("用例3")if __name__ == '__main__':pytest.main()
fixtrue参数详解-autouse
默认False
若为True,每个测试函数都会自动调用该fixture,无需传入fixture函数,作用范围跟着scope走
autouse=ture的效果如下:
测试用例添加、加载、运行(对比unittest)
- 如果要运行多个测试用例,unittest提供了TestSuite/TestLoader的方法加载测试用例,随后利用TextTestRunner执行测试用例套件。
- Pytest 可通过pytest.ini配置文件 确定测试的工作目录。对测试方法、测试类、测试模块的命名进行了约束后,在配置文件中写好名称匹配规则后,通过主函数或者命令行的方式运行。Pytest会自动加载符合条件的测试用例并运行。
pytest跳过测试用例skip、skipif
@pytest.mark.skip
跳过执行测试,有可选参数 reason:跳过的原因,会在执行结果中打印
@pytest.mark.skip可以加在函数上,测试类上,测试类中的方法上
如果加在测试类上面,测试类里面的所有测试用例都不会执行
import pytest@pytest.mark.skip(reason="不执行该用例!!因为没写好!!")
def test_case01():print("skip加在函数上")@pytest.mark.skip(reason="skip加在测试类上")
class TestSkip:def test_1(self):print("%% 不会执行 %%")def test_2(self):print("%% 不会执行 %%")class Test1:@pytest.mark.skip(reason="skip加在测试类中的方法上")def test_1(self):print("%% 不会执行 %%")
@pytest.mark.skipif
方法:
skipif(condition, reason=None)
参数:
condition:跳过的条件,必传参数
reason:标注原因,必传参数
使用方法:
@pytest.mark.skipif(condition, reason=“xxx”)
import pytest
class Test_ABC:def setup_class(self):print("------->setup_class")def teardown_class(self):print("------->teardown_class")def test_a(self):print("------->test_a")assert 1@pytest.mark.skipif(condition=2>1,reason = "跳过该函数")def test_b(self):print("------->test_b")assert 0
执行结果:test_abc.py ------->setup_class------->test_a #只执行了函数test_a.------->teardown_classs # 跳过函数
跳过标记
可以将 pytest.mark.skip 和 pytest.mark.skipif 赋值给一个变量,在不同模块之间共享这个变量。可以用一个单独的文件去管理这些通用标记
# 标记
skipmark = pytest.mark.skip(reason="不能在window上运行=====")
skipifmark = pytest.mark.skipif(sys.platform == 'win32', reason="不能在window上运行啦啦啦=====")@skipmark
class TestSkip_Mark(object):@skipifmarkdef test_function(self):print("测试标记")def test_def(self):print("测试标记")@skipmark
def test_skip():print("测试标记")
Pytest参数化
@pytest.mark.parametrize(argnames, argvalues);# argnames 含义:参数列表
# argvalues 含义:参数值列表# 例如:
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):print(f"测试数据{test_input},期望结果{expected}")assert eval(test_input) == expected
# 参数列表就是"test_input,expected" 与函数中的形参一致。 参数值必须是一个列表,且由于有两个参数,需要以元组的方式存放
# 如果只有一个参数,如:@pytest.mark.parametrize(“username”, [“yy”, “yy2”, “yy3”])
# 如果有多个参数例,则需要用元组来存放值,一个元组对应一组参数的值
函数返回值作为参数
import pytest
def return_test_data():# 中间代码掠过return [(1,2),(0,3)]
class Test_ABC:def setup_class(self):print("------->setup_class")def teardown_class(self):print("------->teardown_class")@pytest.mark.parametrize("a,b",return_test_data()) # 使用函数返回值的形式传入参数值def test_a(self,a,b):print("test data:a=%d,b=%d"%(a,b))assert a+b == 3执行结果:test_abc.py ------->setup_classtest data:a=1,b=2 # 运行第一次取值 a=1,b=2.test data:a=0,b=3 # 运行第二次取值 a=0,b=3.------->teardown_class
“笛卡尔积”,多个参数化装饰器
# 笛卡尔积,组合数据
data_1 = [1, 2, 3]
data_2 = ['a', 'b']
@pytest.mark.parametrize('a', data_1)
@pytest.mark.parametrize('b', data_2)
def test_parametrize_1(a, b):print(f'笛卡尔积 测试数据为 : {a},{b}')
输出测试报告
目前主流的与Pytest搭配的测试报告是Allure,Allure是一个灵活轻量级多语言测试报告工具。
关于Allure会在另外一篇博文中介绍。
预期失败函数
作用
期望测试用例是失败的,但是仍会运行此测试用例,并且也不会影响其他测试用例的的执行。
如果预期失败的测试用例执行失败,则输出结果是xfail(不会额外显示出错误信息)
如果预期失败的测试用例执行成功,则输出结果是xpass。(不符合预期的成功)
应用场景
- 用例功能不完善,或者用例执行一直失败。
- 对尚未实现的功能进行测试时。
- 尚未修复的错误进行测试时。
语法与参数
@pytest.mark.xfail(condition=None, reason=None);
-
condition:表示预期结果,然后用例实际执行的结果,与预期结果对比,会出现4种测试结果状态。
failed, passed, xfailed, xpassed。
提示:condition可以等于True或者False,也可以等于一个表达式,如:condition=1>2等。 -
reason:说明用例标记为预期失败的原因, 默认为None。(必填)
另外,我们也可以通过pytest.xfail方法在用例执行过程中直接标记用例结果为XFAIL,并跳过剩余的部分:
def test_function():if not valid_config():pytest.xfail("failing configuration (but should work)")
控制方法执行顺序
在使用 Pytest 进行单元测试或集成测试时,通常测试用例的执行顺序是自动排序的。不过在某些情况下,特别是当测试用例存在依赖关系时,我们可能希望自定义测试的执行顺序。为了解决这一需求,Pytest 提供了一个实用插件——pytest-ordering。
简介
pytest-ordering 是 Pytest 的一个插件,允许我们自定义测试用例的执行顺序。通过为测试用例指定顺序标记,可以在确保测试独立性的同时,满足特定的执行需求。
安装 pytest-ordering
pip install pytest-ordering
使用方法
在安装插件后,可以使用 @pytest.mark.run 标记来为测试用例设置执行顺序。常用的执行顺序标记有以下几种:
- @pytest.mark.run(order=N):使用具体的整数值 N 定义执行顺序,数字越小,优先级越高。
- @pytest.mark.run(before=‘test_name’):表示当前测试用例应在指定测试 test_name 之前运行。
- @pytest.mark.run(after=‘test_name’):表示当前测试用例应在指定测试 test_name 之后运行。
示例
示例1:基于 order 的执行顺序
import pytest@pytest.mark.run(order=2)
def test_case_1():assert True@pytest.mark.run(order=1)
def test_case_2():assert True@pytest.mark.run(order=3)
def test_case_3():assert True
执行顺序将按照 order 的值来确定,因此运行顺序为:test_case_2 -> test_case_1 -> test_case_3。
示例2:基于 before/after 的执行顺序
import pytest@pytest.mark.run(before="test_case_2")
def test_initial_setup():assert Truedef test_case_1():assert True@pytest.mark.run(after="test_case_1")
def test_case_2():assert True
在此示例中,执行顺序为 test_initial_setup -> test_case_1 -> test_case_2。
失败重试
有时候用例失败并非代码问题,而是由于网络等因素,导致请求失败。从而降低了自动化用例的稳定性,最后还要花时间定位到底是自身case的原因还是业务逻辑问题,还是其他原因,增加了定位成本。
增加容错机制,失败重试,会解决大部分由于网络原因、服务重启等原因造成的case失败问题。那该如何增加失败重试机制呢?使用pytest-rerunfailures插件来实现失败重试功能。
简介
pytest-rerunfailures 是一个基于 pytest 框架的插件,它允许我们对测试用例进行失败重试。当一个测试用例失败时,插件会自动重新运行失败的测试用例,直到达到预定的重试次数或测试用例通过为止。这样可以增加用例的稳定性,并减少因为偶发性问题导致的测试失败。
文档地址:
https://github.com/pytest-dev/pytest-rerunfailures
https://pypi.org/project/pytest-rerunfailures/#description
安装
pip install pytest-rerunfailures
使用方法
装饰器方式
import pytest
# 参数:reruns=n(重新运行次数),reruns_delay=m(每次重试之间的延迟时间)
@pytest.mark.flaky(reruns=3, reruns_delay=2)
def test_case():
assert 1 == 2
运行case,看一下执行结果:
# 执行命令
pytest -s -v test_demo.py::test_case# 结果
RERUN
test_dir/test_demo.py::test_case RERUN
test_dir/test_demo.py::test_case RERUN
test_dir/test_demo.py::test_case FAILED
可以看到,重试了3次,最终结果为失败。
参数添加运行方式
pytest-rerunfailures支持使用命令行选项和配置文件的方式进行配置。
并且是对所有的测试用例生效
# 在命令行运行中添加参数 --reruns 3 --reruns-delay 2
pytest -s -v --reruns 3 --reruns-delay 2 test_demo.py::test_case# 主函数运行中添加参数
pytest.main(["-s", "-v", "--reruns", "3", "--reruns-delay", "2", "test_demo.py::test_case"])# 或者在pytest.ini配置文件addopts中添加参数
addopts=-vs --reruns 3 --reruns-delay 2 --html=./report/report.html
注意:如果指定了用例的重新运行次数,则在命令行添加 --reruns 对这些用例是不会生效的
参考目录
https://www.bilibili.com/video/BV18Q4y1y7v3
https://blog.csdn.net/kkkkk19980517/article/details/139065687
https://blog.csdn.net/lovedingd/article/details/98952868
https://blog.csdn.net/qq_42610167/article/details/101204066
https://blog.csdn.net/qq_45609369/article/details/140007322
https://blog.csdn.net/m0_63463510/article/details/145914339
https://blog.csdn.net/m0_37135615/article/details/146145220
https://blog.csdn.net/fyyaom/article/details/102938704
https://blog.csdn.net/cebawuyue/article/details/144106872
https://blog.csdn.net/weixin_56331124/article/details/145190520