Pytest 使用及調用方法
使用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運算符(and和or)操作。上面的示例將運行TestMyClass.test_something
但不運行TestMyClass.test_method_simple
。
按節點id運行測試
每次執行收集到的測試用例集合都會被分配一個唯一的nodeid
,其中包含模塊文件名,后跟說明符,如類名、函數名及參數,由::
字符分隔。
執行模塊中某條指定的測試用例如:
pytest test_mod.py::test_func
另一個通過命令行挑選所執行測試用例的示例如:
pytest test_mod.py::TestClass::test_method
通過標記(Mark)表達式運行測試
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會自動禁用顯示print輸出, 並捕獲該用例輸出結果:
- 其他測試中的輸出捕獲不受影響。
- 任何先前的測試輸出已經被捕獲並將被處理。
- 在同一測試中生成的任何后續輸出都不會被捕獲,而是直接發送到
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
默認情況下,Pytest不會顯示<0.01s的測試時間, 除非在命令行上傳遞-vv
。
創建JUnit XML格式的測試報告
要創建可由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_property
,record_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()
。