Pytest官方教程-02-使用和调用方法

目录:

  1. 安装及入门
  2. 使用和调用方法
  3. 原有TestSuite使用方法
  4. 断言的编写和报告
  5. Pytest fixtures:清晰 模块化 易扩展
  6. 使用Marks标记测试用例
  7. Monkeypatching/对模块和环境进行Mock
  8. 使用tmp目录和文件
  9. 捕获stdout及stderr输出
  10. 捕获警告信息
  11. 模块及测试文件中集成doctest测试
  12. skip及xfail: 处理不能成功的测试用例
  13. Fixture方法及测试用例的参数化
  14. 缓存: 使用跨执行状态
  15. unittest.TestCase支持
  16. 运行Nose用例
  17. 经典xUnit风格的setup/teardown
  18. 安装和使用插件
  19. 插件编写
  20. 编写钩子(hook)方法
  21. 运行日志
  22. API参考
    1. 方法(Functions)
    2. 标记(Marks)
    3. 钩子(Hooks)
    4. 装置(Fixtures)
    5. 对象(Objects)
    6. 特殊变量(Special Variables)
    7. 环境变量(Environment Variables)
    8. 配置选项(Configuration Options)
  23. 优质集成实践
  24. 片状测试
  25. Pytest导入机制及sys.path/PYTHONPATH
  26. 配置选项
  27. 示例及自定义技巧
  28. Bash自动补全设置

使用和调用方法

使用python -m pytest调用pytest

2.0版本新增
你可以在命令行中通过Python编译器来调用pytest执行测试

python -m pytest [...]

通过python调用会将当前目录也添加到sys.path中,除此之外,这几乎等同于命令行直接调用pytest [...]

可能出现的执行退出code

执行pytest可能会出现6中不同的退出code:

  • 退出code 0: 收集并成功通过所有测试用例
  • 退出code 1: 收集并运行了测试,部分测试用例执行失败
  • 退出code 2: 测试执行被用户中断
  • 退出code 3: 执行测试中发生内部错误
  • 退出code 4: pytest命令行使用错误
  • 退出code 5: 没有收集到测试用例

获取版本路径、命令行选项及环境变量相关帮助

pytest --version   # 显示pytest导入位置
pytest --fixtures  # 显示可用的内置方法参数
pytest -h | --help # 显示命令行及配置文件选项帮助信息

第1(N)次失败后停止测试

在第1(N)次用例失败后停止测试执行:

pytest -x            # 第1次失败后停止
pytest --maxfail=2    # 2次失败后停止

指定及选择测试用例

Pytest支持多种从命令行运行和选择测试用例的方法。
运行模块内所有用例

pytest test_mod.py

运行目录内所有用例

pytest testing/

按关键字表达式运行测试

pytest -k "MyClass and not method"

这将运行包含与给定字符串表达式匹配的名称的测试用例,其中可以包括文件名、类名和函数名作为变量及Python运算符操作。上面的示例将运行TestMyClass.test_something但不运行TestMyClass.test_method_simple
按节点id运行测试
每次执行收集到的测试集合都会被分配一个唯一的nodeid,其中包含模块文件名,后跟说明符,如类名、函数名及参数,由:: 字符分隔。
执行模块中某条指定的测试用例:

pytest test_mod.py::test_func

另一个通过命令行挑选所执行测试方法的示例:

pytest test_mod.py::TestClass::test_method

通过标记表达式运行测试

pytest -m slow

这将会执行所有带@pytest.mark.slow装饰器的用例。
有关更多信息,请参阅标记
从包中运行测试

pytest --pyargs pkg.testing

这将会导入pkg.testing并使用其文件系统位置来查找和运行测试。

修改Python追溯(traceback)信息

修改回追溯信息示例:

pytest --showlocals # 在追溯信息中显示局部变量
pytest -l           # 显示局部变量 (简写)

pytest --tb=auto    # (默认) 第1和最后1条使用详细追溯信息,其他使用简短追溯信息

pytest --tb=long    # 详尽,信息丰富的追溯信息格式
pytest --tb=short   # 简短的追溯信息格式
pytest --tb=line    # 每个失败信息一行
pytest --tb=native  # Python标准库格式
pytest --tb=no      # 不使用追溯信息

详尽的摘要报告

2.9版本新增
-r标志可用于在测试会话结束时显示测试结果摘要,从而可以在大型测试套件中轻松获得所有失败、跳过、标记失败(xfails)等测试结果的清晰图像。
例如:

$ pytest -ra
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items

======================= no tests ran in 0.12 seconds =======================

-r选项接受后面的多个字符,上面使用的a表示“除了执行通过(Pass)以外所有的结果”。
以下是可以使用的可用字符的完整列表:
-f - 失败的用例
-E - 出错的用例
-s - 跳过的用例
-x - 标记失败的用例
-X - 标记成功的用例
-p - 成功用例
-P - 成功用例并输出信息
-a - 所有pP状态以外的用例

可以使用多个字符,例如,只查看失败和跳过的用例,你可以执行:

$ pytest -rfs
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items

======================= no tests ran in 0.12 seconds =======================

执行失败时进入PDB (Python调试器)

Python附带一个名为PDB的内置Python调试器。 pytest允许通过命令行选项进入PDB提示符:

pytest --pdb

这将在每次失败(或KeyboardInterrupt)时调用Python调试器。一般,你可能只希望在第一次失败的测试中执行此操作以了解某种故障情况:

pytest -x --pdb   # 在第一次用例失败时进入PDB
pytest --pdb --maxfail=3  # 在前3次失败是进入PDB

注意,在任何失败时,异常信息都存储在`sys.last_value1,1sys.last_type1和1sys.last_traceback1中。在交互模式中,这允许用户使用任何调试工具进行事后调试。也可以手动访问异常信息,例如:

>>> import sys
>>> sys.last_traceback.tb_lineno
42
>>> sys.last_value
AssertionError('assert result == "ok"',)

测试开始时进入PDB(Python调试器)

pytest允许用户通过命令行选项在每次测试开始时立即进入PDB提示符:

pytest --trace

这将在每次测试开始时调用Python调试器。

设置断点

要在代码中设置断点,需要在代码中使用Python原生import pdb; pdb.set_trace()进行调用,pytest会自动禁用显示并捕获该用例输出结果:

  • 其他测试中的输出捕获不受影响。
  • 任何先前的测试输出已经被捕获并将被处理。
  • 在同一测试中生成的任何后续输出都不会被捕获,而是直接发送到sys.stdout。注意:即使是退出交互式PDB跟踪会话并继续常规测试后发生的测试输出,这也适用。

使用内置断点方法

Python 3.7引入了内置breakpoint()函数。 Pytest支持以下几种使用breakpoint()的方式:

  • PYTHONBREAKPOINT设置为默认值,调用breakpoint()时,pytest将使用其内部PDB跟踪交互界面(PDB trace UI)而不是Python自带的Pdb
  • 测试完成后,默认会重置为Python自带的PDB跟踪交互界面。
  • 在pytest后使用--pdb参数,在失败的测试/未处理异常中,pytest内部PDB跟踪交互界面与breakpoint()同时使用。
  • --pdbcls参数可指定要使用的调试器类。

分析测试执行持续时间

显示执行最慢的10条测试用例:

pytest --durations=10

除非在命令行上传递-vv,默认情况下,否则pytest不会显示<0.01s的测试时间。

创建JUnitXML格式文件

要创建可由Jenkins或其他持续集成软件读取的XML测试报告,可以使用:

pytest --junitxml=path

运行结束后,在指定路径path下创建一个XML报告文件
3.1版本新增
可以通过修改配置中junit_suite_name字段的名称来更改XML报告中root test suite的名称。

[pytest]
junit_suite_name = my_suite

record_property(添加新属性)
版本2.8新增
版本3.5更改: 在所有报告生成器(reporter)中用户属性record_xml_property项已改为record_propertyrecord_xml_property现已弃用。
可以使用record_property项来在XML报告中增加更多的日志信息:

def test_function(record_property):
    record_property("example_key", 1)
    assert True

在生成的testcase标签是会添加一个额外的属性example_key="1"

<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
  <properties>
    <property name="example_key" value="1" />
  </properties>
</testcase>

或者,你可以将此功能集成在自定义标记装饰器中:

# conftest.py文件内容

def pytest_collection_modifyitems(session, config, items):
    for item in items:
        for marker in item.iter_markers(name="test_id"):
            test_id = marker.args[0]
            item.user_properties.append(("test_id", test_id))

在你的测试用例中使用:

# test_function.py文件内容
import pytest

@pytest.mark.test_id(1501)
def test_function():
    assert True

这将导致:

<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
  <properties>
    <property name="test_id" value="1501" />
  </properties>
</testcase>

警告:
record_property是一个实验性功能,将来可能会发生变化。
另外,这将破坏一些XML结构验证,与某些持续集成软件一起使用时,可能会导致一些问题。

record_xml_attribute(修改xml节点属性)
3.4版本新增
可以使用record_xml_attribute fixture向testcase`标签中添加其他xml属性。也可以用来覆盖原有属性值:

def test_function(record_xml_attribute):
    record_xml_attribute("assertions", "REQ-1234")
    record_xml_attribute("classname", "custom_classname")
    print("hello world")
    assert True

record_property不同, 它不会在节点下添加子元素,而是在生成的testcase标签内添加一个属性assertions ="REQ-1234",并使用classname = custom_classname覆盖默认的classname属性:

<testcase classname="custom_classname" file="test_function.py" line="0" name="test_function" time="0.003" assertions="REQ-1234">
    <system-out>
        hello world
    </system-out>
</testcase>

警告:
record_xml_attribute也是一个实验性功能,其界面可能会被更强大,更通用的未来版本所取代。但是,将保留功能本身。

通过使用record_xml_property可以为在使用持续集成工具解析xml报告时提供帮助。 但是,一些解析器对允许的元素和属性非常严格。 许多工具使用xsd模式(如下例所示)来验证传入的xml。 确保使用解析器允许的属性名称。

以下是Jenkins用于验证xml报告的结构:

<xs:element name="testcase">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="skipped" minOccurs="0" maxOccurs="1"/>
            <xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="name" type="xs:string" use="required"/>
        <xs:attribute name="assertions" type="xs:string" use="optional"/>
        <xs:attribute name="time" type="xs:string" use="optional"/>
        <xs:attribute name="classname" type="xs:string" use="optional"/>
        <xs:attribute name="status" type="xs:string" use="optional"/>
    </xs:complexType>
</xs:element>

LogXML: add_global_property
3.0版本新增
如果要在testsuite级别添加属性节点,该节点可能包含与所有测试用例相关的属性,则可以使用LogXML.add_global_properties

import pytest


@pytest.fixture(scope="session")
def log_global_env_facts(f):

    if pytest.config.pluginmanager.hasplugin("junitxml"):
        my_junit = getattr(pytest.config, "_xml", None)

    my_junit.add_global_property("ARCH", "PPC")
    my_junit.add_global_property("STORAGE_TYPE", "CEPH")

@pytest.mark.usefixtures(log_global_env_facts.__name__)
def start_and_prepare_env():
    pass

class TestMe(object):
    def test_foo(self):
        assert True

这会在生成的xml中的testsuite节点下的属性节中添加:

<testsuite errors="0" failures="0" name="pytest" skips="0" tests="1" time="0.006">
  <properties>
    <property name="ARCH" value="PPC"/>
    <property name="STORAGE_TYPE" value="CEPH"/>
  </properties>
  <testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
</testsuite>

警告:
这仍然是一个实验性功能,其界面也可能会被更强大,更通用的未来版本所取代, 但也将保留该功能。

创建结果日志格式文件

3.0版本之后不推荐使用,计划在4.0版本中删除。

对于仍然需要类似功能的用户来说,可以使用提供测试数据流的pytest-tap插件。

如有任何疑虑,可以建立一个问题(open an issue)

pytest --resultlog=path

执行后,在path路径中会创建一个纯文本结果日志文件,这些文件可以用于:例如,在PyPy-test网页显示多个修订版的测试结果。

将测试报告发送到在线pastebin服务

为每条测试失败用例建立一个日志URL链接:

pytest --pastebin=failed

这会将测试运行信息提交到一个提供粘贴服务的远程服务器上,并为每条测试失败用例提供一个URL。 您可以像平常一样查看搜集结果,或者使用-x参数,来只显示某个特定的测试失败结果。

为整个测试执行日志建立一个URL链接:

pytest --pastebin=all

目前只实现了粘贴到http://bpaste.net网站的服务。

禁用插件

可以通过-p选项与前缀no:一起使用,来在运行时禁用加载特定插件。

例如:要禁用加载从文本文件执行doctest测试的doctest插件,可以通过以下方式运行pytest:

pytest -p no:doctest

在Python代码调用pytest

版本2.0新增
你可以在Python代码中直接调用pytest:

pytest.main()

这就和你从命令行调用“pytest”一样。但它不会引发SystemExit,而是返回exitcode。 你可以传入选项和参数。

pytest.main(['-x', 'mytestdir'])

您可以为pytest.main指定其他插件:

# myinvoke.py文件内容
import pytest
class MyPlugin(object):
    def pytest_sessionfinish(self):
        print("*** test run reporting finishing")

pytest.main(["-qq"], plugins=[MyPlugin()])

运行它将显示已添加MyPlugin并调用其中的hook方法:

$ python myinvoke.py
.                                                                    [100%]*** test run reporting finishing 

注意:
调用pytest.main()将会导入所有测试用例及其导入的其他模块。由于python导入系统的缓存机制,从同一进程后续调用pytest.main()不会反映调用之间对这些文件的更改。 因此,不建议从同一进程(例如,为了新运行测试)多次调用pytest.main()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容