目录
通常,pytest是用命令pytest调用的(关于调用 pytest
的其他方式,请参见下文)。这将执行当前目录及其子目录中所有文件名以test_*.py或\*_test.py形式结尾的文件中的所有测试。更一般地说,pytest遵循标准的测试发现规则。
指定要运行哪些测试
在模块中运行测试
pytest test_mod.py
在目录中运行测试
pytest testing/
按关键字表达式运行测试
pytest -k 'MyClass and not method'
这个命令会运行名称与给定字符串表达式(不区分大小写)匹配的测试,该表达式可以包含使用文件名、类名和函数名作为变量的Python运算符。上面的例子会运行 TestMyClass.test_something
但不会运行 TestMyClass.test_method_simple
。如果你在Windows上运行此命令,请在表达式中使用双引号 ""
而不是单引号 ''
。
按集合参数运行测试
传递相对于工作目录的模块文件名,后面跟着由 ::
字符分隔的指定符,如类名和函数名,以及用 []
包围的参数化参数。
在模块中运行特定的测试:
pytest tests/test_mod.py::test_func
运行类中的所有测试:
pytest tests/test_mod.py::TestClass
指定特定测试方法的:
pytest tests/test_mod.py::TestClass::test_method
指定测试的特定参数的:
pytest tests/test_mod.py::test_func[x1,y2]
表示要运行 tests/test_mod.py
文件中名为 test_func
的测试函数,并且这个函数是通过参数化(可能使用了 @pytest.mark.parametrize
装饰器)定义的,这里 [x1,y2]
指定了参数化测试的一个具体参数组合。
按标记表达式运行测试
pytest -m slow
将运行所有使用 @pytest.mark.slow
装饰器装饰的测试。有关更多信息,请参阅标记(Marks)。
从包运行测试
pytest --pyargs pkg.testing
这将导入pkg.testing
,并使用其文件系统位置来查找并运行其中的测试。
从文件中读取参数
在 8.2 版本中新添加功能。
上述所有内容都可以使用 @
前缀从一个文件中读取:
pytest @tests_to_run.txt
其中tests_to_run.txt每行包含一个条目,例如:
tests/test_file.py
tests/test_mod.py::test_func[x1,y2]
tests/test_mod.py::TestClass
-m slow
这个文件也可以通过使用 pytest --collect-only -q
命令生成,并根据需要进行修改。pytest --collect-only -q
命令用于收集测试,但不实际运行它们。-q
选项表示“quiet”模式,即减少输出信息。这个命令会列出所有可收集的测试项(即 pytest
能够找到的测试),但不会执行它们。你可以将这个命令的输出重定向到一个文件中,然后编辑该文件来指定你想要运行的测试。这对于快速生成一个包含所有可用测试的列表,并从中选择你想要运行的特定测试非常有用。
获取关于版本、选项名、环境变量的帮助
pytest --version #显示pytest是从哪里导入的
pytest --fixtures #显示可用的内置函数参数
pytest -h | --help #在命令行和配置文件选项中显示帮助
分析测试执行时间
要获取超过1.0秒的最慢的10个测试持续时间列表:
pytest --durations=10 --durations-min=1.0
默认情况下,除非在命令行中传递了 -vv
(表示更详细的输出),否则 pytest
不会显示耗时过短(<0.005秒)的测试项持续时间。
管理插件的加载
提前加载插件
你可以在命令行中使用-p选项显式地提前加载插件(内部和外部):
pytest -p mypluginmodule
该选项接收一个名称参数,它可以是:
用点分隔的完整模块名称,例如myproject.plugins。这个带点的名称必须是可导入的。
插件的入口点名称。这是注册插件时传递给setuptools的名称。例如,要提前加载pytest-cov插件,你可以使用:
pytest -p pytest_cov
pytest_cov
是一个插件,它用于在 pytest
测试期间收集代码覆盖率信息。当你运行 pytest -p pytest_cov
时,pytest
会在测试开始之前加载 pytest_cov
插件,这样它就可以在测试执行过程中收集代码覆盖率数据。测试完成后,pytest_cov
插件通常会报告覆盖率统计信息,包括哪些代码被执行了,哪些没有。
禁用插件
要在调用时禁用加载特定插件,请使用-p选项和前缀no:。
示例:要禁用加载负责从文本文件执行 doctest 测试的插件 doctest
,可以像这样调用 pytest
:
pytest -p no:doctest
调用pytest的其他方法
通过python - m pytest
调用pytest
你可以在命令行中通过Python解释器调用测试:
Python -m pytest[…]
这几乎等同于直接调用命令行脚本 pytest [...]
,但通过 Python 调用还会将当前目录添加到 sys.path
中。
sys.path
是一个列表,包含了 Python 解释器在查找模块时会搜索的路径。当你通过 python -m pytest
这种方式来运行 pytest
时,Python 解释器会首先执行 pytest
模块,并且因为它是以模块的方式被调用的,所以 Python 会自动将当前目录(即你运行命令时所在的目录)添加到 sys.path
中。这样做的好处是,如果你的测试脚本或模块位于当前目录下,或者依赖于当前目录下的其他模块,那么它们就可以被正确地导入和执行了。
从Python代码调用pytest
你可以直接从Python代码中调用pytest:
retcode = pytest.main()
这样做就好像你从命令行调用“pytest”一样。它不会引发SystemExit
异常,而是返回退出代码。如果你没有给它传递任何参数,main
函数会从进程的命令行参数(sys.argv
)中读取参数,这可能不是你想要的。你可以显式地传递选项和参数:
retcode = pytest.main(["-x", "mytestdir"])
在这个例子中,pytest.main()
函数被调用,并传递了一个包含选项和参数的列表。这里,"-x"
选项表示在第一个测试失败时立即停止测试,"mytestdir"
是包含测试文件的目录名。pytest.main()
函数执行测试,并返回一个整数退出代码,其中0
表示成功,非0
值表示有测试失败或发生了其他错误。
你还可以向 pytest.main
指定额外的插件:
# content of myinvoke.py
import sys
import pytest
class MyPlugin:
def pytest_sessionfinish(self):
print("*** test run reporting finishing")
if __name__ == "__main__":
sys.exit(pytest.main(["-qq"], plugins=[MyPlugin()]))
运行这段代码会显示 MyPlugin
已被添加,并且它的钩子函数被调用了:
$ python myinvoke.py
*** test run reporting finishing
定义了一个名为 MyPlugin
的类,它有一个方法 pytest_sessionfinish
,这是一个 pytest 钩子函数,它会在测试会话结束时被调用。然后,在脚本的 if __name__ == "__main__":
块中,我们调用了 pytest.main()
函数,并将一个包含 -qq
选项的列表和包含 MyPlugin
实例的列表作为参数传递给它。-qq
选项用于减少 pytest 的输出,只显示必要的测试摘要信息。
通过向 pytest.main()
传递 plugins
参数,我们可以将自定义的插件或钩子添加到 pytest 测试会话中。在这个例子中,当测试会话结束时,pytest_sessionfinish
钩子函数会被调用,打印出 "*** test run reporting finishing"
消息。
请注意
调用pytest.main()将导致导入测试和它们导入的任何模块。由于python导入系统的缓存机制,从同一进程对pytest.main()的后续调用将不会反映调用之间对这些文件的更改。出于这个原因,不建议从同一进程多次调用pytest.main()(例如,为了重新运行测试)。
为了避免这个问题,你可以考虑以下几种方法:
-
在单独的进程中运行测试:每次需要运行测试时,都启动一个新的 Python 进程。这可以通过在命令行中直接调用
pytest
命令或使用 Python 的subprocess
模块来实现。 -
清理 Python 的模块缓存:在调用
pytest.main()
之前,你可以尝试清除 Python 的模块缓存,但这通常不是推荐的做法,因为它可能会导致不可预测的行为和性能问题。 -
使用 pytest 的内置功能:如果你正在编写一个需要多次运行测试的脚本,考虑是否可以使用 pytest 的其他功能(如测试套件、测试集合或参数化测试)来满足你的需求,而不是多次调用
pytest.main()
。