书籍来源:房荔枝 梁丽丽《pytest框架与自动化测试应用》
一边学习一边整理老师的课程内容及实验笔记,并与大家分享,侵权即删,谢谢支持!
附上汇总贴:pytest框架进阶自学系列 | 汇总_热爱编程的通信人的博客-CSDN博客
测试用例的运行管理基本通过pytest命令行选项来完成各种运行情况处理。先通过帮助来看一看有哪些命令行选项,下面详细讲解常用的几个选项。
注意:所有的命令执行都在终端下运行。
获取帮助信息
以下是pytest --fixtures的帮助返回信息。
代码如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest --fixtures
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning
collected 29 items
cache
Return a cache object that can persist state between testing sessions.
cache.get(key, default)
cache.set(key, value)
Keys must be a ``/`` separated value, where the first part is usually the
name of your plugin or application to avoid clashes with other cache users.
Values can be any object handled by the json stdlib module.
capsys
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsys.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
capsysbinary
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
method calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``bytes`` objects.
capfd
Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
capfdbinary
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``byte`` objects.
doctest_namespace [session scope]
Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests.
pytestconfig [session scope]
Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
Example::
def test_foo(pytestconfig):
if pytestconfig.getoption("verbose") > 0:
...
record_property
Add an extra properties the calling test.
User properties become part of the test report and are available to the
configured reporters, like JUnit XML.
The fixture is callable with ``(name, value)``, with value being automatically
xml-encoded.
Example::
def test_function(record_property):
record_property("example_key", 1)
record_xml_attribute
Add extra xml attributes to the tag for the calling test.
The fixture is callable with ``(name, value)``, with value being
automatically xml-encoded
record_testsuite_property [session scope]
Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to
writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family.
This is a ``session``-scoped fixture which is called with ``(name, value)``. Example:
.. code-block:: python
def test_foo(record_testsuite_property):
record_testsuite_property("ARCH", "PPC")
record_testsuite_property("STORAGE_TYPE", "CEPH")
``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.
caplog
Access and control log capturing.
Captured logs are available through the following properties/methods::
* caplog.messages -> list of format-interpolated log messages
* caplog.text -> string containing formatted log output
* caplog.records -> list of logging.LogRecord instances
* caplog.record_tuples -> list of (logger_name, level, message) tuples
* caplog.clear() -> clear captured records and formatted log output string
monkeypatch
The returned ``monkeypatch`` fixture provides these
helper methods to modify objects, dictionaries or os.environ::
monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=False)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
All modifications will be undone after the requesting
test function or fixture has finished. The ``raising``
parameter determines if a KeyError or AttributeError
will be raised if the set/deletion operation has no target.
recwarn
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
See http://docs.python.org/library/warnings.html for information
on warning categories.
tmpdir_factory [session scope]
Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
tmp_path_factory [session scope]
Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.
tmpdir
Return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
tmp_path
Return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a :class:`pathlib.Path`
object.
.. note::
in python < 3.6 this is a pathlib2.Path
======================================================================================================== no tests ran in 0.16s ========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
最常用运行测试用例方式
后面不加任何参数和文件时,pytest自动收集当前路径下的所有符合要求的测试用例并执行。例如:pytest执行的是第2章chapter-2文件夹所有测试用例的执行详细信息。
执行结果如下,这部分结果同时也包括2.4.1节实现的执行结果,可以比对是否与结果一致。
有些效果是安装了一些插件才有的,插件安装将在第5章详细介绍。
通过python -m pytest调用pytest
可以通过Python的解释器来执行测试,命令如下:
但是,这和直接执行pytest [...]命令的效果几乎一模一样。
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> python -m pytest .\test_assert_1.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning
collected 4 items
test_assert_1.py FFFF [100%]
============================================================================================================== FAILURES ===============================================================================================================
______________________________________________________________________________________________________ test_long_str_comparison _______________________________________________________________________________________________________
def test_long_str_comparison():
str3 = 'abcdef'
str4 = 'adcdef'
> assert str3 == str4
E AssertionError: assert 'abcdef' == 'adcdef'
E - adcdef
E ? ^
E + abcdef
E ? ^
test_assert_1.py:4: AssertionError
____________________________________________________________________________________________________________ test_eq_list _____________________________________________________________________________________________________________
def test_eq_list():
> assert [0,1,2] == [0,1,3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
E Use -v to get the full diff
test_assert_1.py:7: AssertionError
________________________________________________________________________________________________________ test_dict_comparison _________________________________________________________________________________________________________
def test_dict_comparison():
dict1 = {
'name': 'linda',
'age': 18,
}
dict2 = {
'name': 'linda',
'age': 88,
}
> assert dict1 == dict2
E AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'age': 18} != {'age': 88}
E Use -v to get the full diff
test_assert_1.py:18: AssertionError
_________________________________________________________________________________________________________ test_set_comparison _________________________________________________________________________________________________________
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
E '5'
E Use -v to get the full diff
test_assert_1.py:23: AssertionError
======================================================================================================= short test summary info =======================================================================================================
FAILED test_assert_1.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
FAILED test_assert_1.py::test_eq_list - assert [0, 1, 2] == [0, 1, 3]
FAILED test_assert_1.py::test_dict_comparison - AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
FAILED test_assert_1.py::test_set_comparison - AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
========================================================================================================== 4 failed in 0.07s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
这是在终端下执行的,上段代码最后一行是执行命令,%号前面是计算机用户信息(lindafang@linda)和所在位置(chapter-2),以及后面的命令(python -m pytest test_assert_1.py)。
在Python代码中调用pytest
可以直接在代码中调用pytest,在main()函数中调用pytest.main()与在命令行中执行pytest的效果几乎是一样的,本书在第1章中就使用了这种方式调用并执行的。
pytest.main()方法中的选项和参数不同,这也将影响执行的结果。具体看下面的例子。
- 例一:传递选项和参数pytest.main(['-q',__file__])。
pytest.main(['-q',__file__])中q是参数quiet的简写,表示静默执行,__file__是获得当前文件名,也是要执行的文件,但它不会触发SystemExit,而是返回exitcode。
执行结果如下:
import time
def test_one():
time.sleep(10)
if __name__ == '__main__':
import pytest
ret = pytest.main(['-q', __file__])
print("pytest.main() 返回 pytest.ExitCode.INTERRUPTED: ", ret == pytest.ExitCode.INTERRUPTED)
可以选择两种执行方式,不同的执行方式在中断后信息显示略有不同。
第一种,在PyCharm中使用pytest框架执行这个文件。
用例中有等待10s的操作,在这期间,打断停止执行,pytest.main()返回的是INTERRUPTED状态码:KeyboardInterrupt。
执行结果如下:
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_invoke_via_main.py
Testing started at 13:52 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_invoke_via_main.py in D:\SynologyDrive\CodeLearning\WIN\pytest_learning
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest_learning\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning
collecting ... collected 1 item
test_invoke_via_main.py::test_one
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_invoke_via_main.py:4: KeyboardInterrupt
(to show a full traceback on KeyboardInterrupt use --full-trace)
============================ no tests ran in 3.96s ============================
Process finished with exit code 2
- 第二种,选择终端执行Python文件名,它们之间有所不同,后者可以打印出输出信息。
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> python .\test_invoke_via_main.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_invoke_via_main.py:4: KeyboardInterrupt
(to show a full traceback on KeyboardInterrupt use --full-trace)
no tests ran in 0.53s
pytest.main() 返回 pytest.ExitCode.INTERRUPTED: True
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> ^C
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
- 例二:传递选项和参数:pytest.main(["-qq"],plugins=[MyPlugin()])指定一个插件。
代码如下:
import pytest
class MyPlugin:
def pytest_sessionfinish(self):
import allure
allure.title("笔者导入了allure")
print("allure test run reporting finishing")
pytest.main(["-qq"], plugins=[MyPlugin])
由于pytest.main不在if __name__=='__main__':的下面,选择在终端执行,执行结果参考如下,因为没有选择要执行的文件,执行的范围是配置的path所在的范围或者所在的目录下的所有用例。有可能只想执行插件加载,结果执行了所有测试用例。
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> python .\test_main_plugin.py
FFFF.FF.FF...FFF.F...........F [100%]allure test run reporting finishing
============================================================================================================== FAILURES ===============================================================================================================
______________________________________________________________________________________________________ test_long_str_comparison _______________________________________________________________________________________________________
def test_long_str_comparison():
str3 = 'abcdef'
str4 = 'adcdef'
> assert str3 == str4
E AssertionError: assert 'abcdef' == 'adcdef'
E - adcdef
E ? ^
E + abcdef
E ? ^
test_assert_1.py:4: AssertionError
____________________________________________________________________________________________________________ test_eq_list _____________________________________________________________________________________________________________
def test_eq_list():
> assert [0,1,2] == [0,1,3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
E Full diff:
E - [0, 1, 3]
E ? ^
E + [0, 1, 2]
E ? ^
test_assert_1.py:7: AssertionError
________________________________________________________________________________________________________ test_dict_comparison _________________________________________________________________________________________________________
def test_dict_comparison():
dict1 = {
'name': 'linda',
'age': 18,
}
dict2 = {
'name': 'linda',
'age': 88,
}
> assert dict1 == dict2
E AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'age': 18} != {'age': 88}
E Full diff:
E - {'age': 88, 'name': 'linda'}
E ? ^
E + {'age': 18, 'name': 'linda'}...
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
test_assert_1.py:18: AssertionError
_________________________________________________________________________________________________________ test_set_comparison _________________________________________________________________________________________________________
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
E '5'
E Full diff:
E - {'5', '3', '8', '0'}
E ? -----...
E
E ...Full output truncated (3 lines hidden), use '-vv' to show
test_assert_1.py:23: AssertionError
______________________________________________________________________________________________________________ test_true ______________________________________________________________________________________________________________
def test_true():
assert (1<3) is True
> assert (5>3) is False
E assert (5 > 3) is False
test_assert_3.py:7: AssertionError
____________________________________________________________________________________________________________ test_in_dict _____________________________________________________________________________________________________________
def test_in_dict():
x = 'linda1'
> assert x in "I'm linda"
E assert 'linda1' in "I'm linda"
test_assert_3.py:11: AssertionError
___________________________________________________________________________________________________________ test_long_list ____________________________________________________________________________________________________________
def test_long_list():
x = [str(i) for i in range(100)]
y = [str(i) for i in range(0, 100, 2)]
> assert x == y
E AssertionError: assert ['0', '1', '2...'4', '5', ...] == ['0', '2', '4...8', '10', ...]
E At index 1 diff: '1' != '2'
E Left contains 50 more items, first extra item: '50'
E Full diff:
E [
E '0',
E + '1',
E '2',...
E
E ...Full output truncated (99 lines hidden), use '-vv' to show
test_assert_3.py:20: AssertionError
______________________________________________________________________________________________________________ test_long ______________________________________________________________________________________________________________
def test_long():
> assert [12]*50 == [11]*50
E AssertionError: assert [12, 12, 12, 12, 12, 12, ...] == [11, 11, 11, 11, 11, 11, ...]
E At index 0 diff: 12 != 11
E Full diff:
E [
E - 11,
E ? ^
E + 12,
E ? ^...
E
E ...Full output truncated (198 lines hidden), use '-vv' to show
test_assert_3.py:23: AssertionError
_______________________________________________________________________________________________________ test_approx_simple_fail _______________________________________________________________________________________________________
def test_approx_simple_fail():
> assert 0.1 + 0.2 == approx(0.35)
E assert (0.1 + 0.2) == 0.35 ± 3.5e-07
E + where 0.35 ± 3.5e-07 = approx(0.35)
test_assert_sample.py:18: AssertionError
____________________________________________________________________________________________________ test_warrior_long_description ____________________________________________________________________________________________________
def test_warrior_long_description():
desc = get_long_class_description("warrior")
> assert (
desc
== textwrap.dedent(
"""\
A seasoned veteran of many battles. Strength and Dexterity
allow to yield heavy armor and weapons, as well as carry
more equipment. Weak in magic.
"""
)
)
E AssertionError: assert 'A seasoned v...k in magic.\n' == 'A seasoned v...k in magic.\n'
E - A seasoned veteran of many battles. Strength and Dexterity
E + A seasoned veteran of many battles. High Strength and Dexterity
E ? +++++
E allow to yield heavy armor and weapons, as well as carry
E - more equipment. Weak in magic.
E + more equipment while keeping a light roll. Weak in magic.
test_assert_sample.py:32: AssertionError
_____________________________________________________________________________________________________ test_get_starting_equiment ______________________________________________________________________________________________________
def test_get_starting_equiment():
expected = ["长剑", "战士装备"]
> assert get_starting_equipment("战士") == expected, "装备不符"
E AssertionError: 装备不符
E assert ['长剑', '战士装备', '盾'] == ['长剑', '战士装备']
E Left contains one more item: '盾'
E Full diff:
E - ['长剑', '战士装备']
E + ['长剑', '战士装备', '盾']
E ? +++++
test_assert_sample.py:49: AssertionError
__________________________________________________________________________________________________________ test_foo_compare ___________________________________________________________________________________________________________
def test_foo_compare():
f1 = Foo(1)
f2 = Foo(2)
> assert f1 == f2
E assert 比较两个Foo实例:
E 值: 1 != 2
test_fail_cus.py:12: AssertionError
_____________________________________________________________________________________________________________ test_match ______________________________________________________________________________________________________________
def test_match():
with pytest.raises((ValueError, RuntimeError),match=r'.*40011.*'):
> myfunc()
test_test.py:8:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def myfunc():
> raise ValueError("返回40013支付错误")
E ValueError: 返回40013支付错误
test_test.py:4: ValueError
During handling of the above exception, another exception occurred:
def test_match():
with pytest.raises((ValueError, RuntimeError),match=r'.*40011.*'):
> myfunc()
E AssertionError: Pattern '.*40011.*' does not match '返回40013支付错误'
test_test.py:8: AssertionError
========================================================================================================== warnings summary ===========================================================================================================
C:\Users\guoliang\AppData\Local\Programs\Python\Python37\lib\site-packages\_pytest\terminal.py:289
C:\Users\guoliang\AppData\Local\Programs\Python\Python37\lib\site-packages\_pytest\terminal.py:289: PytestDeprecationWarning: 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.
"TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.\n"
-- Docs: https://docs.pytest.org/en/latest/warnings.html
======================================================================================================= short test summary info =======================================================================================================
FAILED test_assert_1.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
FAILED test_assert_1.py::test_eq_list - assert [0, 1, 2] == [0, 1, 3]
FAILED test_assert_1.py::test_dict_comparison - AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
FAILED test_assert_1.py::test_set_comparison - AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
FAILED test_assert_3.py::test_true - assert (5 > 3) is False
FAILED test_assert_3.py::test_in_dict - assert 'linda1' in "I'm linda"
FAILED test_assert_3.py::test_long_list - AssertionError: assert ['0', '1', '2...'4', '5', ...] == ['0', '2', '4...8', '10', ...]
FAILED test_assert_3.py::test_long - AssertionError: assert [12, 12, 12, 12, 12, 12, ...] == [11, 11, 11, 11, 11, 11, ...]
FAILED test_assert_sample.py::test_approx_simple_fail - assert (0.1 + 0.2) == 0.35 ± 3.5e-07
FAILED test_assert_sample.py::test_warrior_long_description - AssertionError: assert 'A seasoned v...k in magic.\n' == 'A seasoned v...k in magic.\n'
FAILED test_assert_sample.py::test_get_starting_equiment - AssertionError: 装备不符
FAILED test_fail_cus.py::test_foo_compare - assert 比较两个Foo实例:
FAILED test_test.py::test_match - AssertionError: Pattern '.*40011.*' does not match '返回40013支付错误'
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
- 例三:传递选项和参数:pytest .main(["-x","mytestdir"]),执行到1个失败便停止。这是“-x”参数的作用,遇到失败就不再往下执行了。
在test_assert_2.py中加入后执行。
代码如下:
import pytest
def test_long_str_comparison():
str3 = 'abcdef'
str4 = 'adcdef'
assert str3 == str4
def test_eq_list():
assert [0,1,2] == [0,1,3]
def test_dict_comparison():
dict1 = {
'name': 'linda',
'age': 18,
}
dict2 = {
'name': 'linda',
'age': 88,
}
assert dict1 == dict2
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
assert set1 == set2
if __name__ == '__main__':
pytest.main(["-x", "test_assert_2.py"])
执行结果如下:4个测试用例都有错误,收集到4个,在第一个测试用例执行之后就停止。
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> python .\test_assert_2.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning
plugins: allure-pytest-2.13.2
collected 4 items
test_assert_2.py F
============================================================================================================== FAILURES ===============================================================================================================
______________________________________________________________________________________________________ test_long_str_comparison _______________________________________________________________________________________________________
def test_long_str_comparison():
str3 = 'abcdef'
str4 = 'adcdef'
> assert str3 == str4
E AssertionError: assert 'abcdef' == 'adcdef'
E - adcdef
E ? ^
E + abcdef
E ? ^
test_assert_2.py:5: AssertionError
======================================================================================================= short test summary info =======================================================================================================
FAILED test_assert_2.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================================================================================== 1 failed in 0.04s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> ]
注意:调用pytest.main()会引入测试文件及其引用的所有模块。由于Python引入机制的缓存特性,当这些文件发生变化时,后续再调用pytest.main()(在同一个程序执行过程中)时,并不会响应这些文件的变化。
基于这个原因,一般不推荐在同一个程序中多次调用pytest.main()(例如:为了重新执行测试。如果确实有这个需求,则可以考虑pytest-repeat插件)。
pytest执行结束时返回的状态码
pytest命令执行结束,可能会返回以下6种状态码:
ExitCode 0:(OK)所有收集到的用例测试通过;
ExitCode 1:(TESTS_FAILED)用例测试失败;
ExitCode 2:(INTERRUPTED)用户打断测试执行;
ExitCode 3:(INTERNAL_ERROR)测试执行的过程中,发生内部错误;
ExitCode 4:(USAGE_ERROR)pytest命令使用错误;
ExitCode 5:(NO_TESTS_COLLECTED)没有收集到测试用例。
它们在枚举类_pytest .main.ExitCode中声明,并且其作为公开API的一部分,能够直接引入和访问,代码如下:
输出代码中的控制台信息
pytest -s可以输出代码中的控制台信息。
显示详细信息
pytest -v可以显示更详细的信息,可以多个参数连在一起使用。
显示详细信息包括控制台输出信息,代码如下:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -s -v .\test_raise.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 1 item
test_raise.py::test_match 如果不是解释器请求退出,则这个原因所引起的异常会被测试认定为不通过
FAILED
============================================================================================================== FAILURES ===============================================================================================================
_____________________________________________________________________________________________________________ test_match ______________________________________________________________________________________________________________
def test_match():
with pytest.raises(ImportError):
print("如果不是解释器请求退出,则这个原因所引起的异常会被测试认定为不通过")
> f()
test_raise.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def f():
> raise SystemExit(1)
E SystemExit: 1
test_raise.py:4: SystemExit
======================================================================================================= short test summary info =======================================================================================================
FAILED test_raise.py::test_match - SystemExit: 1
========================================================================================================== 1 failed in 0.12s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
不显示详细信息
pytest -q命令不显示太详细的信息。q是quiet的缩写,表示静默。
显示简单总结结果
-r选项可以在执行结束后打印一个简短的总结报告。当执行的测试用例很多时,可以对结果有个清晰的了解:在终端输入:pytest -q -rA。
执行结果如下:
-r选项后面要紧接这个参数,用于过滤显示测试用例的结果。
以下是所有有效的字符参数:
- f:失败的;
- E:出错的;
- s:跳过执行的;
- x:跳过执行,并标记为xfailed的;
- X:跳过执行,并标记为xpassed的;
- p:测试通过的;
- P:测试通过,并且有输出信息的,即用例中有print等;
- a:除了测试通过的,其他所有的,即除了p和P的;
- A:所有的。
上述字符参数可以叠加使用,例如:执行后期望过滤掉失败的和跳过执行的(-rfs)。
代码如下:
执行指定的测试用例
pytest支持多种方式执行特定的测试用例,这些丰富的形式让测试用例的运行及管理变得更灵活实用。
执行指定模块(文件)中的测试用例,代码如下:
执行指定目录下所有的测试用例
如果在某个文件夹下执行该文件夹下所有测试用例,则可以直接输入pytest或者输入pytest ../文件夹名。
如果在某个文件夹上一级执行该文件夹下所有测试用例,则可以直接输入pytest 文件夹名/,代码如下:
-k参数执行包含特定关键字的测试用例
-k参数主要搜索测试方法函数类名中包含与不包含的名字,并可进行与或非的组合逻辑搜索。
执行当前目录下名字包含Case但不包含three的测试用例:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -vk "Case and not three"
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 42 items / 40 deselected / 2 selected
test_frame.py::TestCase::test_four PASSED [ 50%]
test_frame_2.py::TestCase::test_four PASSED [100%]
========================================================================================================== warnings summary ===========================================================================================================
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289: PytestDeprecationWarning: 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.
"TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.\n"
-- Docs: https://docs.pytest.org/en/latest/warnings.html
============================================================================================ 2 passed, 40 deselected, 1 warning in 10.47s =============================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
执行指定文件中名字包含TestCase(类名)和three(方法名)的测试用例:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest .\test_frame.py -k "TestCase and three"
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 4 items / 3 deselected / 1 selected
test_frame.py . [100%]
=================================================================================================== 1 passed, 3 deselected in 0.02s ===================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
执行当前路径(chapter-1)下名字包含TestCase或者test_one的函数名、文件名、方法名、类名的用例:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -v -k "TestCase or test_one"
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 42 items / 32 deselected / 10 selected
test_frame.py::test_one PASSED [ 10%]
test_frame.py::TestCase::test_three PASSED [ 20%]
test_frame.py::TestCase::test_four PASSED [ 30%]
test_frame_1.py::test_one PASSED [ 40%]
test_frame_2.py::TestCase::test_three PASSED [ 50%]
test_frame_2.py::TestCase::test_four PASSED [ 60%]
test_invoke_via_main.py::test_one PASSED [ 70%]
test_nodeid.py::test_one PASSED [ 80%]
test_nodeid.py::TestNodeId::test_one PASSED [ 90%]
test_one.py::test_one PASSED [100%]
========================================================================================================== warnings summary ===========================================================================================================
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289
c:\users\guoliang\appdata\local\programs\python\python37\lib\site-packages\_pytest\terminal.py:289: PytestDeprecationWarning: 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.
"TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.\n"
-- Docs: https://docs.pytest.org/en/latest/warnings.html
============================================================================================ 10 passed, 32 deselected, 1 warning in 20.59s ============================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
执行指定文件中名字包含comparison的用例:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest .\test_assert_2.py -k "comparison"
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 4 items / 1 deselected / 3 selected
test_assert_2.py FFF [100%]
============================================================================================================== FAILURES ===============================================================================================================
______________________________________________________________________________________________________ test_long_str_comparison _______________________________________________________________________________________________________
def test_long_str_comparison():
str3 = 'abcdef'
str4 = 'adcdef'
> assert str3 == str4
E AssertionError: assert 'abcdef' == 'adcdef'
E - adcdef
E ? ^
E + abcdef
E ? ^
test_assert_2.py:5: AssertionError
________________________________________________________________________________________________________ test_dict_comparison _________________________________________________________________________________________________________
def test_dict_comparison():
dict1 = {
'name': 'linda',
'age': 18,
}
dict2 = {
'name': 'linda',
'age': 88,
}
> assert dict1 == dict2
E AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'age': 18} != {'age': 88}
E Use -v to get the full diff
test_assert_2.py:19: AssertionError
_________________________________________________________________________________________________________ test_set_comparison _________________________________________________________________________________________________________
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
E '5'
E Use -v to get the full diff
test_assert_2.py:24: AssertionError
======================================================================================================= short test summary info =======================================================================================================
FAILED test_assert_2.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
FAILED test_assert_2.py::test_dict_comparison - AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
FAILED test_assert_2.py::test_set_comparison - AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
=================================================================================================== 3 failed, 1 deselected in 0.06s ===================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
注意:Python的关键字不可以应用在-k选项中,例如,class、def等。
执行指定nodeid的测试用例
pytest为每个收集到的测试用例指定一个唯一的nodeid。其由模块名加说明符构成,中间以::间隔。其中,说明符可以是类名、函数名。可以通过这种用例唯一的nodeid执行指定的测试用例。
示例代码如下,下面创建了3个测试用例,分别对应不同的说明符。
import pytest
def test_one():
print('test_one')
assert 1
class TestNodeId:
def test_one(self):
print('TestNodeId::test_one')
assert 1
@pytest.mark.parametrize('x,y', [(1,1),(3,4)])
def test_two(self, x, y):
print(f'TestNodeId::test_two::{x} == {y}')
assert x == y
test_one测试方法是函数,是TestNodeId类中的test_one类中的方法,此外test_two是带有参数化的可执行多次的测试方法。通常使用类名和方法名进行标识,参数化的测试方法使用参数数据进行标识。
执行结果如图2-8所示。
指定某个函数名执行,命令如下:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -q -s .\test_nodeid.py::test_one
test_one
.
1 passed in 0.07s
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_nodeid.py
Testing started at 15:52 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_nodeid.py in D:\SynologyDrive\CodeLearning\WIN\pytest_learning
============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest_learning\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning
plugins: allure-pytest-2.13.2
collecting ... collected 4 items
test_nodeid.py::test_one PASSED [ 25%]test_one
test_nodeid.py::TestNodeId::test_one
test_nodeid.py::TestNodeId::test_two[1-1]
test_nodeid.py::TestNodeId::test_two[3-4] PASSED [ 50%]TestNodeId::test_one
PASSED [ 75%]TestNodeId::test_two::1 == 1
FAILED [100%]TestNodeId::test_two::3 == 4
test_nodeid.py:10 (TestNodeId.test_two[3-4])
3 != 4
Expected :4
Actual :3
<Click to see difference>
self = <test_nodeid.TestNodeId object at 0x0000028616AC5948>, x = 3, y = 4
@pytest.mark.parametrize('x,y', [(1,1),(3,4)])
def test_two(self, x, y):
print(f'TestNodeId::test_two::{x} == {y}')
> assert x == y
E assert 3 == 4
E +3
E -4
test_nodeid.py:14: AssertionError
================================== FAILURES ===================================
__________________________ TestNodeId.test_two[3-4] ___________________________
self = <test_nodeid.TestNodeId object at 0x0000028616AC5948>, x = 3, y = 4
@pytest.mark.parametrize('x,y', [(1,1),(3,4)])
def test_two(self, x, y):
print(f'TestNodeId::test_two::{x} == {y}')
> assert x == y
E assert 3 == 4
E +3
E -4
test_nodeid.py:14: AssertionError
---------------------------- Captured stdout call -----------------------------
TestNodeId::test_two::3 == 4
=========================== short test summary info ===========================
FAILED test_nodeid.py::TestNodeId::test_two[3-4] - assert 3 == 4
========================= 1 failed, 3 passed in 0.05s =========================
Process finished with exit code 1
指定某个文件中的某个类名+函数名执行,命令如下:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -q -s .\test_nodeid.py::TestNodeId::test_one
TestNodeId::test_one
.
1 passed in 0.01s
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
注意:这里也可以使用-k选项以便达到同样的效果。
首先,可以使用--collect-only选项查看用例名。
执行及结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -q -s --collect-only .\test_nodeid.py
test_nodeid.py::test_one
test_nodeid.py::TestNodeId::test_one
test_nodeid.py::TestNodeId::test_two[1-1]
test_nodeid.py::TestNodeId::test_two[3-4]
no tests ran in 0.01s
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
然后,使用-k执行符合规则的用例,例如:执行test_nodeid.py::test_one:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -q -s -k 'test_one and not TestNodeId' .\test_nodeid.py
test_one
.
1 passed, 3 deselected in 0.01s
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
结果和执行pytest -q -s test_nodeid.py::test_one相同。
-m参数执行指定标记的用例
-m参数全称为marker,可以通过方法名称的表达式灵活选择测试某种方法和不测试一些方法,举例说明如下。
创建一个新的Python文件test_markers.py,代码如下:
import pytest
@pytest.mark.p0
def test_something_p0():
pass
@pytest.mark.func
def test_something_func():
pass
@pytest.mark.postgres
def test_something_postgres():
pass
运行脚本中所有方法,输入下面的命令并执行。
运行脚本中标记为p0的测试方法,输入下面的命令并执行。
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -v -m 'p0' .\test_markers.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning
plugins: allure-pytest-2.13.2
collected 3 items / 2 deselected / 1 selected
test_markers.py::test_something_p0 PASSED [100%]
========================================================================================================== warnings summary ===========================================================================================================
test_markers.py:3
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_markers.py:3: PytestUnknownMarkWarning: Unknown pytest.mark.p0 - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
@pytest.mark.p0
test_markers.py:7
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_markers.py:7: PytestUnknownMarkWarning: Unknown pytest.mark.func - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
@pytest.mark.func
test_markers.py:11
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_markers.py:11: PytestUnknownMarkWarning: Unknown pytest.mark.postgres - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
@pytest.mark.postgres
-- Docs: https://docs.pytest.org/en/latest/warnings.html
============================================================================================= 1 passed, 2 deselected, 3 warnings in 0.11s =============================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
执行结果中会有pytestUnknownMarkWarning的警告信息,可以忽略,此外可以在配置文件中配置自定义的标记,配置后警告信息会消失。
配置自定义标记的步骤如下:
(1)新建pytest.ini文件。
(2)在文件中输入以下内容。
(3)p0、func、postgres应与test_markers.py文件测试方法中所标记的意义一致,并写清自定义的标记所表示的意思。
(4)再次执行pytest -v -m 'p0' test_markers.py。
(5)执行,正常情况下结果中不再显示警告信息。
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -v -m 'p0' .\test_markers.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 3 items / 2 deselected / 1 selected
test_markers.py::test_something_p0 PASSED [100%]
=================================================================================================== 1 passed, 2 deselected in 0.01s ===================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
运行脚本中不是p0的所有测试方法,输入pytest -v -m 'not p0' test_markers.py命令并执行,其结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -v -m 'not p0' .\test_markers.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 3 items / 1 deselected / 2 selected
test_markers.py::test_something_func PASSED [ 50%]
test_markers.py::test_something_postgres PASSED [100%]
=================================================================================================== 2 passed, 1 deselected in 0.01s ===================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
运行脚本中不是p0也不是func的所有测试方法,输入pytest -v -m 'not p0 and not func' test_markers.py命令并执行,其结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -v -m 'not p0 and not func' .\test_markers.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 3 items / 2 deselected / 1 selected
test_markers.py::test_something_postgres PASSED [100%]
=================================================================================================== 1 passed, 2 deselected in 0.01s ===================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
运行脚本中func或是postgres的测试方法,输入pytest -v -m 'func or postgres'test_markers.py命令并执行,其结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest -v -m 'func or postgres' .\test_markers.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- c:\users\guoliang\appdata\local\programs\python\python37\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 3 items / 1 deselected / 2 selected
test_markers.py::test_something_func PASSED [ 50%]
test_markers.py::test_something_postgres PASSED [100%]
=================================================================================================== 2 passed, 1 deselected in 0.01s ===================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
执行指定包中的测试用例
有时要测试的用例不在常用的文件夹中,此时可以通过指定包来执行包所能收集到的测试用例。执行命令pytest --pyargs pkg.testing;此命令中的pytest会引入pkg.testing包,并在它的系统目录下搜寻测试用例并执行。
Python包的布局及建立步骤如下。
先在pytest-book下创建文件setup.py,代码如下:
from setuptools import setup, find_packages
setup(name="utils", packages=find_packages())
(1)在src同级目录下建立utils的包。
在下面创建max.py文件,代码如下:
def max(values):
_max = values[0]
for val in values:
if val > max:
_max = val
return _max
(2)在utils下创建tests的包。
在tests文件夹创建测试max.py的测试文件test_max.py,代码如下:
def test_max():
values = (2,3,1,4,6,7)
val = max(values)
assert val == 6
(3)整体的布局如下:
(4)最后通过执行包pytest --pyargs utils执行测试用例:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning\pytest-book\src> pytest --pyargs utils
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 1 item
utils\tests\test_max.py F [100%]
============================================================================================================== FAILURES ===============================================================================================================
______________________________________________________________________________________________________________ test_max _______________________________________________________________________________________________________________
def test_max():
values = (2,3,1,4,6,7)
val = max(values)
> assert val == 6
E assert 7 == 6
utils\tests\test_max.py:5: AssertionError
======================================================================================================= short test summary info =======================================================================================================
FAILED utils\tests\test_max.py::test_max - assert 7 == 6
========================================================================================================== 1 failed in 0.13s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning\pytest-book\src>
修改回溯信息的输出模式
pytest回溯信息的输出一共有6种模式:auto、long、short、line、native、no,用--tb选项指定:
--full-trace是一种比--tb=long更详细的输出模式。它甚至能观察到用户打断执行(Ctrl+C)时的回溯信息,而上述6种模式默认是不输出此类信息的。
输入命令pytest--tb=line test_assert_2.py并执行,每个失败信息总结在一行中。
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest --tb=line .\test_assert_2.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 4 items
test_assert_2.py FFFF [100%]
============================================================================================================== FAILURES ===============================================================================================================
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_assert_2.py:5: AssertionError: assert 'abcdef' == 'adcdef'
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_assert_2.py:8: assert [0, 1, 2] == [0, 1, 3]
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_assert_2.py:19: AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
D:\SynologyDrive\CodeLearning\WIN\pytest_learning\test_assert_2.py:24: AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
======================================================================================================= short test summary info =======================================================================================================
FAILED test_assert_2.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
FAILED test_assert_2.py::test_eq_list - assert [0, 1, 2] == [0, 1, 3]
FAILED test_assert_2.py::test_dict_comparison - AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
FAILED test_assert_2.py::test_set_comparison - AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
========================================================================================================== 4 failed in 0.05s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>
选项--tb=no不打印具体失败信息,输入下面命令执行:
执行结果如下:
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning> pytest --tb=no .\test_assert_2.py
========================================================================================================= test session starts =========================================================================================================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest_learning, inifile: pytest.ini
plugins: allure-pytest-2.13.2
collected 4 items
test_assert_2.py FFFF [100%]
======================================================================================================= short test summary info =======================================================================================================
FAILED test_assert_2.py::test_long_str_comparison - AssertionError: assert 'abcdef' == 'adcdef'
FAILED test_assert_2.py::test_eq_list - assert [0, 1, 2] == [0, 1, 3]
FAILED test_assert_2.py::test_dict_comparison - AssertionError: assert {'age': 18, 'name': 'linda'} == {'age': 88, 'name': 'linda'}
FAILED test_assert_2.py::test_set_comparison - AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
========================================================================================================== 4 failed in 0.05s ==========================================================================================================
PS D:\SynologyDrive\CodeLearning\WIN\pytest_learning>