pytest 的简介与安装
pytest 的特点
- 简单灵活,容易上手,文档丰富
- 支持参数化
- 能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium等自动化测试、接口自动化测试(pytest+requests)
- pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-selenium(集成selenium)、pytest-html(完美html测试报告生成)、pytest-rerunfailures(失败case重复执行)、pytest-xdist(多CPU分发)等
- 测试用例的skip和xfail处理
- 支持运行unittest编写的测试case
- 可以很好的和jenkins集成
- 支持 allure 报告
pytest 的安装与验证
- pytest 的安装
pip install pytest
- 验证 pytest 的安装
pytest --version
pytest 与 unittest 对比
unitest | pytest | |
---|---|---|
测试用例 | 需继承unitest.TestCase类 | 无需继承,支持面向函数,面向对象的测试 |
参数化 | 参数化需依赖第三方插件 bdd | 参数化支持两种方式? |
插件 | 没有插件 | 有很丰富的插件 |
失败重试 | 不支持失败重试 | 支持失败重试 |
断言 | 断言使用unittest 断言方法 | 使用 python 自带 assert 方法 |
报告 | 报告使用 HTMLTestRunner | 报告支持 html,allure |
测试用例收集 | 收集用例使用 discover 方法 | 支持按文件名,类名,测试用例名去运行 |
扩展性 | 扩展性不强 | 扩展性强 |
pytest 简单使用
- 使用 pycharm 普通运行方式输出
/usr/local/bin/python3.7 /Users/mac/Desktop/download/UIAutoTest/DemoPytest/test_simple.py
============================= test session starts ==============================
platform darwin -- Python 3.7.8, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 -- /usr/local/opt/python/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/mac/Desktop/download/UIAutoTest/DemoPytest
plugins: rerunfailures-9.1.1, allure-pytest-2.8.16
collecting ... ============================= test session starts ==============================
platform darwin -- Python 3.7.8, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 -- /usr/local/opt/python/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/mac/Desktop/download/UIAutoTest/DemoPytest
plugins: rerunfailures-9.1.1, allure-pytest-2.8.16
collecting ... collected 3 items
test_simple.py::test_001 PASSED
test_simple.py::TestDemo::test_002 PASSED
test_simple.py::TestDemo::test_003 FAILED
=================================== FAILURES ===================================
______________________________ TestDemo.test_003 _______________________________
self = <DemoPytest.test_simple.TestDemo object at 0x10abd6550>
def test_003(self):
> assert 3 == 4
E assert 3 == 4
E +3
E -4
test_simple.py:18: AssertionError
=========================== short test summary info ============================
FAILED test_simple.py::TestDemo::test_003 - assert 3 == 4
========================= 1 failed, 2 passed in 0.10s ==========================
collected 3 items
test_simple.py::test_001 PASSED
test_simple.py::TestDemo::test_002 PASSED
test_simple.py::TestDemo::test_003 FAILED
=================================== FAILURES ===================================
______________________________ TestDemo.test_003 _______________________________
self = <DemoPytest.test_simple.TestDemo object at 0x10abe8f50>
def test_003(self):
> assert 3 == 4
E assert 3 == 4
E +3
E -4
test_simple.py:18: AssertionError
=============================== warnings summary ===============================
/usr/local/lib/python3.7/site-packages/_pytest/compat.py:333
/usr/local/lib/python3.7/site-packages/_pytest/compat.py:333: PytestDeprecationWarning: The TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.
See https://docs.pytest.org/en/latest/deprecations.html#terminalreporter-writer for more information.
return getattr(object, name, default)
-- Docs: https://docs.pytest.org/en/latest/warnings.html
=========================== short test summary info ============================
FAILED test_simple.py::TestDemo::test_003 - assert 3 == 4
==================== 1 failed, 2 passed, 1 warning in 0.19s ====================
Process finished with exit code 0
- 使用 pytest 运行方式运行
- pytest 方式运行会自动搜索测试用例,无法生成测试报告
- 如何修改运行方式呢?
pytest 的常用命令行参数
- 参数:-s(用于显示测试用例中的 print()信息)
- 参数:-v(用于详细显示测试用例的执行过程)
- 参数:-q(简化控制台输出,只显示整体测试结果)
- 参数:-x(运行到失败的用例就停止)
- 参数:--maxfail=x(出现 x 个失败用例既停止)
- 参数:--collect-only(收集将要执行的用例,但不会执行用例)
- 参数:-k(关键字过滤,可过滤文件名,类名,方法名/函数名)
- 参数: -m (只运行带有对应标识的测试用例,需使用@pytest.mark.xxxx进行修饰,后面跟自己的标识,一般进行标记冒烟测试,系统测试,登录测试等)
- 参数:--result-log=path(将执行结果保存到指定路径下)
# -*- conding:utf-8 -*-
# @Author : shanshan
# @File : test_simple.py
import pytest
class TestDemo():
def test_001(self):
assert 2 == 2
def test_002(self):
assert 2 == 3
@pytest.mark.smoking
def test_003(self):
assert 3 == 3
pytest.main(['test_simple.py', '--result-log=/Users/mac/Desktop/download/UIAutoTest/DemoPytest/a.txt'])
-
参数:--junit-xml=path (输出xml文件格式,在与jenkins做集成时使用)
-
参数:--durations=num (显示执行最慢的测试用例,一般用于调优,注意如果要显示执行时间为 0 秒的用例时,要加上参数-vv)
-
参数:-ff (先执行完上次失败的测试后,再执行上次正常的测试)
-
参数: -lf (只运行上次运行失败的用例)
- 参数:-l (用例运行失败时,打印用例中的局部变量)
pytest常用的第三方插件
失败重试
- 安装:
pip install -U pytest-rerunfailures
- 使用
html 报告
- 安装:
pip3 install pytest-html
- 使用
pytest控制函数执行顺序
- 安装:
pip3 install pytest-ordering
- 使用方法
1.标记于被测试函数,@pytest.mark.run(order=x)
2.根据order传入的参数来解决运行顺序
3.order值全为正数或全为负数时,运行顺序:值越小,优先级越高
4.正数和负数同时存在:正数优先级高
注:默认情况下,pytest是根据测试方法名由小到大执行的,可以通过第三方插件包改变其运行顺序。
多重校验
- 安装:
pip3 install pytest-assume
- 使用:
用例依赖
- 安装
pip install pytest-dependency
-
说明:
-
使用该插件可以标记一个testcase作为其他testcase的依赖,当依赖项执行失败时,那些依赖它的test将会被跳过。
-
用 @pytest.mark.dependency()对所依赖的方法进行标记,使用@pytest.mark.dependency(depends=["test_name"])引用依赖,test_name可以是多个。
-
使用:
分布式执行
- 安装
pip3 install pytest-xdist
- 说明:
- 平常我们功能测试用例非常多时,比如有1千条用例,假设每个用例执行需要1分钟,如果单个测试人员执行需要1000分钟才能跑完
- 当项目非常紧急时,会需要协调多个测试资源来把任务分成两部分,于是执行时间缩短一半,如果有10个小伙伴,那么执行时间就会变成十分之一,大大节省了测试时间
- 为了节省项目测试时间,10个测试同时并行测试,这就是一种分布式场景
- 分布式执行用例的原则
- 用例之间是独立的,没有依赖关系,完全可以独立运行
- 用例执行没有顺序要求,随机顺序都能正常执行
- 每个用例都能重复运行,运行结果不会影响其他用例
- 使用
pytest.main(['test_simple.py','-n=2'])
pytest.main(['test_simple.py','-n=auto'])
- 注意:要根据自己电脑的 cpu 数指定,电脑不好肯定会直接让电脑卡死
重复执行用例
- 安装
pip3 install pytest-repeat
-
说明:
-
pytest-repeat是pytest的一个插件,用于重复执行单个用例,或多个测试用例,并指定重复次数
-
使用:
- --repeat-scope
-
function
(默认)范围针对每个用例重复执行,再执行下一个用例 -
class
以class为用例集合单位,重复执行class里面的用例,再执行下一个 -
module
以模块为单位,重复执行模块里面的用例,再执行下一个 -
session
重复整个测试会话,即所有收集的测试执行一次,然后所有这些测试再次执行等等 -
注:--repeat-scope类似于pytest fixture的scope参数,执行完 scope 的参数单位后才会去执行下一个单位的。
-
如果要在代码中标记要重复多次的测试,可以使用
@pytest.mark.repeat(count)
装饰器 -
重复测试直到失败停止
pytest.main(['test_simple.py::TestDemo::test_001','-x','--count=10000'])
重复运行test_001用例 10000 次,直到失败时停止
常用插件地址
pytest 的 fixture
fixture 相比setup和teardown的优势
- 命名方式灵活,不局限于setup和teardown这几个命名
- conftest 可以配置数据共享,
- scope支持四个级别参数 "function" (默认), "class", "module" or "session"
- 可通过 yield语句为测试函数区分前置条件和后置清理
使用1,在当前测试用例的文件中使用
使用 2,使用conftest.py 文件
- conftest.py配置需要注意以下点:
- conftest.py配置脚本名称是固定的,不能改名称
- conftest.py与运行的用例要在同一个pakage下,并且有__init__.py文件
- 不需要import导入 conftest.py,pytest用例会自动查找
多个 fixture 之间的相互调用
- 建议多使用 fixture,很多测试用例需要前置条件,后置清理时,它是你的不二之选
pytest 的参数化
(1)pytest.mark.parametrize装饰器
-
实质上就是数据驱动测试 DDT
-
当参数化应用在类上时,则此时类的所有测试方法都将使用参数化中的变量
-
在使用参数化的过程中也可以使用标记,比如标记为fail或者skip
(2)使用fixture进行参数化
- (3)参数化之JSON 文件
- 参数化之 Excel文件,Yaml文件道理都是一样的,读到的数据是一个列表,列表里面嵌套列表,字典,元祖都可以,但是要和参数名称一一对应起来哦
# 创建一个 json 文件
{
"login_param":
[
{
"username": "aaa",
"password": "aaa"
},
{
"username": "bbb",
"password": "bbb"
},
{
"username": "ccc",
"password": "ccc"
},
{
"username": "ddd",
"password": "ddd1"
}
]
}
# conftest 文件代码
def read_login_json():
with open("login.json", 'r', encoding="UTF-8") as f:
data = json.load(f)['login_param']
return data
# json参数化
@pytest.fixture(params=read_login_json())
def login_params(request): # 参数 request固定写法
return request.param # requests.param 固定写法
# test_simple.py 文件
class TestDemo(object):
def test_002(self, login_params):
assert login_params["username"] == login_params["password"]
pytest.main(['test_simple.py::TestDemo',
'-v', ])
pytest 的 hook 函数
什么是 hook 函数
- 比如说你写了一个框架类的程序,你希望这个框架可以“被其他的代码注入”,即别人可以加入代码对你这个框架进行定制化,该如何做比较好?
- 一种很常见的方式就是约定一个规则,框架初始化时会收集满足这个规则的所有代码(文件),然后把这些代码加入到框架中来,在执行时一并执行。所有这一规则下可以被框架收集到的方法就是hook方法
pytest 加载插件的方式
-
内置plugins:从代码内部的_pytest目录加载
-
外部插件(第三方插件):通过setuptools entry points机制发现的第三方插件模块;
推荐的第三方的pytest的插件:https://docs.pytest.org/en/latest/plugins.html
-
conftest.py形式的本地插件:测试目录下的自动模块发现机制
通过pytest --trace-config命令可以查看当前pytest中所有的plugin。
编写自己的插件
命令行添加自定义参数
# conftest.py 文件
# 注册一个命令行参数--cmdopt
def pytest_addoption(parser):
parser.addoption(
"--cmdopt",
action="store",
default="devices_info",
help="What is needed to start the session udid and port,type is dict"
)
# 创建一个 fixture 获取命令行参数--cmdopt 的值
@pytest.fixture() # session 作用域的话,每次执行用例不会重启 app
def cmdopt(request):
return request.config.getoption("--cmdopt")
# test_simple.py 文件
class TestDemo(object):
def test_004(self, cmdopt):
print("获取到的设备名称:{}".format(cmdopt))
assert 4 == 4
def test_005(self, cmdopt):
print("获取到的设备名称:{}".format(cmdopt))
assert 4 == 5
pytest.main(['test_simple.py::TestDemo',
'-vs', '--cmdopt=iphone'])
collecting ... ============================= test session starts ==============================
platform darwin -- Python 3.7.8, pytest-6.2.1, py-1.9.0, pluggy-0.13.1 -- /usr/local/opt/python/bin/python3.7
cachedir: .pytest_cache
metadata: {'Python': '3.7.8', 'Platform': 'Darwin-19.6.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.1', 'py': '1.9.0', 'pluggy': '0.13.1'}, 'Plugins': {'assume': '2.4.2', 'rerunfailures': '9.1.1', 'html': '3.1.1', 'allure-pytest': '2.8.16', 'metadata': '1.11.0', 'dependency': '0.5.1', 'ordering': '0.6', 'repeat': '0.9.1', 'xdist': '2.2.0', 'forked': '1.3.0'}}
rootdir: /Users/mac/Desktop/download/UIAutoTest/DemoPytest
plugins: assume-2.4.2, rerunfailures-9.1.1, html-3.1.1, allure-pytest-2.8.16, metadata-1.11.0, dependency-0.5.1, ordering-0.6, repeat-0.9.1, xdist-2.2.0, forked-1.3.0
collecting ... collected 2 items
test_simple.py::TestDemo::test_004 获取到的设备名称:iphone
PASSED
test_simple.py::TestDemo::test_005 获取到的设备名称:iphone
FAILED
=================================== FAILURES ===================================
______________________________ TestDemo.test_005 _______________________________
self = <DemoPytest.test_simple.TestDemo object at 0x10576e0d0>
cmdopt = 'iphone'
def test_005(self, cmdopt):
print("获取到的设备名称:{}".format(cmdopt))
> assert 4 == 5
E assert 4 == 5
E +4
E -5
test_simple.py:46: AssertionError
=========================== short test summary info ============================
FAILED test_simple.py::TestDemo::test_005 - assert 4 == 5
========================= 1 failed, 1 passed in 0.10s ==========================
collected 2 items
test_simple.py::TestDemo::test_004 获取到的设备名称:iphone
PASSED
test_simple.py::TestDemo::test_005 获取到的设备名称:iphone
FAILED
-
parser.addoption()参数说明
name:自定义命令行参数的名字,可以是:“foo”, “-foo” 或 “–foo”; action:在命令行中遇到此参数时要采取的基本操作类型; nargs:应该使用的命令行参数的数量; const:某些操作和nargs选择所需的常量值; default:如果参数不在命令行中,则生成的默认值。 type:命令行参数应该转换为的类型; choices:参数允许值的容器; required:命令行选项是否可以省略(仅可选); help:对参数作用的简要说明; metavar:用法消息中参数的名称; dest:要添加到 parse_args() 返回的对象中的属性的名称;
-
使用场景
- 使用 UI 自动化时,可以指定手机设备,或者不同的浏览器
- 接口自动化时,可以指定正式环境,测试环境分别读取不同的配置文件数据
获取每个用例详细的执行结果
- pytest_runtest_markreport()
- 可以获取到测试用例的详细执行结果,setup 是否执行成功,call 测试用例是否执行成功,teardown 是否执行成功
# conftest.py 文件
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
print('*'*50)
# 获取钩子方法的调用结果
out = yield
print('用例执行结果', out)
# 从钩子方法的调用结果中获取测试报告
report = out.get_result()
print('测试报告:{}'.format(report))
print('步骤:{}'.format(report.when))
print('nodeid:{}'.format(report.nodeid))
print('description:{}'.format(str(item.function.__doc__)))
print(('运行结果: {}'.format(report.outcome)))
print('*'*50)
# 与上面一样,只是通过 when 进行了阶段的区分
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
print('*' * 50)
# 获取钩子方法的调用结果
out = yield
print('用例执行结果', out)
# 从钩子方法的调用结果中获取测试报告
report = out.get_result()
# 通过 when 来区分 setup,call,teardown 这三个阶段
if report.when == "call":
print('测试报告:{}'.format(report))
print('步骤:{}'.format(report.when))
print('nodeid:{}'.format(report.nodeid))
print('description:{}'.format(str(item.function.__doc__)))
print(('运行结果: {}'.format(report.outcome)))
print('*' * 50)
# test_simple.py 文件
class TestDemo(object):
def test_004(self, cmdopt):
assert 4 == 4
def test_005(self, cmdopt):
assert 4 == 5
pytest.main(['test_simple.py::TestDemo',
'-v'])
/usr/local/bin/python3.7 /Users/mac/Desktop/download/UIAutoTest/DemoPytest/test_simple.py
============================= test session starts ==============================
platform darwin -- Python 3.7.8, pytest-6.2.1, py-1.9.0, pluggy-0.13.1 -- /usr/local/opt/python/bin/python3.7
cachedir: .pytest_cache
metadata: {'Python': '3.7.8', 'Platform': 'Darwin-19.6.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.1', 'py': '1.9.0', 'pluggy': '0.13.1'}, 'Plugins': {'assume': '2.4.2', 'rerunfailures': '9.1.1', 'html': '3.1.1', 'allure-pytest': '2.8.16', 'metadata': '1.11.0', 'dependency': '0.5.1', 'ordering': '0.6', 'repeat': '0.9.1', 'xdist': '2.2.0', 'forked': '1.3.0'}}
rootdir: /Users/mac/Desktop/download/UIAutoTest/DemoPytest
plugins: assume-2.4.2, rerunfailures-9.1.1, html-3.1.1, allure-pytest-2.8.16, metadata-1.11.0, dependency-0.5.1, ordering-0.6, repeat-0.9.1, xdist-2.2.0, forked-1.3.0
collecting ... collected 2 items
test_simple.py::TestDemo::test_004 **************************************************
用例执行结果 <pluggy.callers._Result object at 0x1053d5650>
测试报告:<TestReport 'test_simple.py::TestDemo::test_004' when='setup' outcome='passed'>
步骤:setup
nodeid:test_simple.py::TestDemo::test_004
description:None
运行结果: passed
**************************************************
**************************************************
用例执行结果 <pluggy.callers._Result object at 0x105413050>
测试报告:<TestReport 'test_simple.py::TestDemo::test_004' when='call' outcome='passed'>
步骤:call
nodeid:test_simple.py::TestDemo::test_004
description:None
运行结果: passed
**************************************************
PASSED [ 50%]**************************************************
用例执行结果 <pluggy.callers._Result object at 0x105413050>
测试报告:<TestReport 'test_simple.py::TestDemo::test_004' when='teardown' outcome='passed'>
步骤:teardown
nodeid:test_simple.py::TestDemo::test_004
description:None
运行结果: passed
**************************************************
test_simple.py::TestDemo::test_005 **************************************************
用例执行结果 <pluggy.callers._Result object at 0x1053bba10>
测试报告:<TestReport 'test_simple.py::TestDemo::test_005' when='setup' outcome='passed'>
步骤:setup
nodeid:test_simple.py::TestDemo::test_005
description:None
运行结果: passed
**************************************************
**************************************************
用例执行结果 <pluggy.callers._Result object at 0x105454710>
测试报告:<TestReport 'test_simple.py::TestDemo::test_005' when='call' outcome='failed'>
步骤:call
nodeid:test_simple.py::TestDemo::test_005
description:None
运行结果: failed
**************************************************
FAILED [100%]**************************************************
用例执行结果 <pluggy.callers._Result object at 0x105438d10>
测试报告:<TestReport 'test_simple.py::TestDemo::test_005' when='teardown' outcome='passed'>
步骤:teardown
nodeid:test_simple.py::TestDemo::test_005
description:None
运行结果: passed
**************************************************
=================================== FAILURES ===================================
______________________________ TestDemo.test_005 _______________________________
self = <DemoPytest.test_simple.TestDemo object at 0x105426790>
def test_005(self):
> assert 4 == 5
E assert 4 == 5
E +4
E -5
test_simple.py:44: AssertionError
=========================== short test summary info ============================
FAILED test_simple.py::TestDemo::test_005 - assert 4 == 5
========================= 1 failed, 1 passed in 0.23s ==========================
Process finished with exit code 0
改变用例执行顺序
-
pytest_collection_modifyitems(session, items)
-
可以通过此 hook 函数改变收集到用例的执行顺序
# conftest.py 文件
def pytest_collection_modifyitems(session, items):
print("收集到的测试用例:%s" % items)
random.shuffle(items) # 将收集到的用例进行随机
print("随机排序后的用例:%s" % items)
# test_simple.py 文件
class TestDemo(object):
def test_004(self):
assert 4 == 4
def test_b_002(self):
assert 1 == 1
def test_a_002(self):
assert 1 == 1
def test_005(self):
assert 4 == 5
def test_b_001(self):
assert 1 == 1
def test_a_001(self):
assert 1 == 1
pytest.main(['test_simple.py::TestDemo',
'-v'])
/usr/local/bin/python3.7 /Users/mac/Desktop/download/UIAutoTest/DemoPytest/test_simple.py
============================= test session starts ==============================
platform darwin -- Python 3.7.8, pytest-6.2.1, py-1.9.0, pluggy-0.13.1 -- /usr/local/opt/python/bin/python3.7
cachedir: .pytest_cache
metadata: {'Python': '3.7.8', 'Platform': 'Darwin-19.6.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.1', 'py': '1.9.0', 'pluggy': '0.13.1'}, 'Plugins': {'assume': '2.4.2', 'rerunfailures': '9.1.1', 'html': '3.1.1', 'allure-pytest': '2.8.16', 'metadata': '1.11.0', 'dependency': '0.5.1', 'ordering': '0.6', 'repeat': '0.9.1', 'xdist': '2.2.0', 'forked': '1.3.0'}}
rootdir: /Users/mac/Desktop/download/UIAutoTest/DemoPytest
plugins: assume-2.4.2, rerunfailures-9.1.1, html-3.1.1, allure-pytest-2.8.16, metadata-1.11.0, dependency-0.5.1, ordering-0.6, repeat-0.9.1, xdist-2.2.0, forked-1.3.0
collecting ... 收集到的测试用例:[<Function test_004>, <Function test_b_002>, <Function test_a_002>, <Function test_005>, <Function test_b_001>, <Function test_a_001>]
随机排序后的用例:[<Function test_b_001>, <Function test_004>, <Function test_b_002>, <Function test_005>, <Function test_a_001>, <Function test_a_002>]
collected 6 items
test_simple.py::TestDemo::test_b_001 PASSED [ 16%]
test_simple.py::TestDemo::test_004 PASSED [ 33%]
test_simple.py::TestDemo::test_b_002 PASSED [ 50%]
test_simple.py::TestDemo::test_005 FAILED [ 66%]
test_simple.py::TestDemo::test_a_001 PASSED [ 83%]
test_simple.py::TestDemo::test_a_002 PASSED [100%]
=================================== FAILURES ===================================
______________________________ TestDemo.test_005 _______________________________
self = <DemoPytest.test_simple.TestDemo object at 0x1110799d0>
def test_005(self):
> assert 4 == 5
E assert 4 == 5
E +4
E -5
test_simple.py:50: AssertionError
=========================== short test summary info ============================
FAILED test_simple.py::TestDemo::test_005 - assert 4 == 5
========================= 1 failed, 5 passed in 0.26s ==========================
Process finished with exit code 0
统计测试结果
- pytest_terminal_summary(terminalreporter, exitstatus, config)
- 通过此 hook 函数可以获取到用例执行的结果,通过数,失败数,跳过数,总数,执行时间等
# conftest.py
# 收集测试结果
def pytest_terminal_summary(terminalreporter, exitstatus, config):
print("收集测试结果开始:")
print("*"*30)
print(terminalreporter.stats)
print("total:", terminalreporter._numcollected)
print('passed:', len(terminalreporter.stats.get('passed', [])))
print('failed:', len(terminalreporter.stats.get('failed', [])))
print('error:', len(terminalreporter.stats.get('error', [])))
print('skipped:', len(terminalreporter.stats.get('skipped', [])))
# terminalreporter._sessionstarttime 会话开始时间
duration = time.time() - terminalreporter._sessionstarttime
print('total times:', duration, 's')
print("收集测试结果结束:")
print("*" * 30)
# test_simple.py 文件
class TestDemo(object):
def test_004(self):
assert 4 == 4
def test_b_002(self):
assert 1 == 1
def test_a_002(self):
assert 1 == 1
def test_005(self):
assert 4 == 5
def test_b_001(self):
assert 1 == 1
def test_a_001(self):
assert 1 == 1
pytest.main(['test_simple.py::TestDemo',
'-v'])
/usr/local/bin/python3.7 /Users/mac/Desktop/download/UIAutoTest/DemoPytest/test_simple.py
============================= test session starts ==============================
platform darwin -- Python 3.7.8, pytest-6.2.1, py-1.9.0, pluggy-0.13.1 -- /usr/local/opt/python/bin/python3.7
cachedir: .pytest_cache
metadata: {'Python': '3.7.8', 'Platform': 'Darwin-19.6.0-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.1', 'py': '1.9.0', 'pluggy': '0.13.1'}, 'Plugins': {'assume': '2.4.2', 'rerunfailures': '9.1.1', 'html': '3.1.1', 'allure-pytest': '2.8.16', 'metadata': '1.11.0', 'dependency': '0.5.1', 'ordering': '0.6', 'repeat': '0.9.1', 'xdist': '2.2.0', 'forked': '1.3.0'}}
rootdir: /Users/mac/Desktop/download/UIAutoTest/DemoPytest
plugins: assume-2.4.2, rerunfailures-9.1.1, html-3.1.1, allure-pytest-2.8.16, metadata-1.11.0, dependency-0.5.1, ordering-0.6, repeat-0.9.1, xdist-2.2.0, forked-1.3.0
collecting ... collected 6 items
test_simple.py::TestDemo::test_004 PASSED [ 16%]
test_simple.py::TestDemo::test_b_002 PASSED [ 33%]
test_simple.py::TestDemo::test_a_002 PASSED [ 50%]
test_simple.py::TestDemo::test_005 FAILED [ 66%]
test_simple.py::TestDemo::test_b_001 PASSED [ 83%]
test_simple.py::TestDemo::test_a_001 PASSED [100%]
=================================== FAILURES ===================================
______________________________ TestDemo.test_005 _______________________________
self = <DemoPytest.test_simple.TestDemo object at 0x110dbfe50>
def test_005(self):
> assert 4 == 5
E assert 4 == 5
E +4
E -5
test_simple.py:50: AssertionError
收集测试结果开始:
******************************
{'': [<TestReport 'test_simple.py::TestDemo::test_004' when='setup' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_004' when='teardown' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_b_002' when='setup' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_b_002' when='teardown' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_a_002' when='setup' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_a_002' when='teardown' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_005' when='setup' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_005' when='teardown' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_b_001' when='setup' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_b_001' when='teardown' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_a_001' when='setup' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_a_001' when='teardown' outcome='passed'>], 'passed': [<TestReport 'test_simple.py::TestDemo::test_004' when='call' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_b_002' when='call' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_a_002' when='call' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_b_001' when='call' outcome='passed'>, <TestReport 'test_simple.py::TestDemo::test_a_001' when='call' outcome='passed'>], 'failed': [<TestReport 'test_simple.py::TestDemo::test_005' when='call' outcome='failed'>]}
total: 6
passed: 5
failed: 1
error: 0
skipped: 0
total times: 0.48371005058288574 seconds
收集测试结果结束:
******************************
=========================== short test summary info ============================
FAILED test_simple.py::TestDemo::test_005 - assert 4 == 5
========================= 1 failed, 5 passed in 0.48s ==========================
Process finished with exit code 0