目錄:
- 安裝及入門
- 使用和調用方法
- 原有TestSuite使用方法
- 斷言的編寫和報告
- Pytest fixtures:清晰 模塊化 易擴展
- 使用Marks標記測試用例
- Monkeypatching/對模塊和環境進行Mock
- 使用tmp目錄和文件
- 捕獲stdout及stderr輸出
- 捕獲警告信息
- 模塊及測試文件中集成doctest測試
- skip及xfail: 處理不能成功的測試用例
- Fixture方法及測試用例的參數化
- 緩存: 使用跨執行狀態
- unittest.TestCase支持
- 運行Nose用例
- 經典xUnit風格的setup/teardown
- 安裝和使用插件
- 插件編寫
- 編寫鉤子(hook)方法
- 運行日志
- API參考
- 優質集成實踐
- 片狀測試
- Pytest導入機制及sys.path/PYTHONPATH
- 配置選項
- 示例及自定義技巧
- Bash自動補全設置
使用pip安裝包
對于開發,我們建議你將venv用于虛擬環境(或者用于Python 2.7的virtualenv),并使用 pip來安裝應用程序和任何依賴項,以及pytest
包本身。這可確保你的代碼和依賴項與系統Python安裝隔離。
接下來,setup.py
使用以下最低內容將文件放在包的根目錄中:
from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())
PACKAGENAME
包裹的名稱在哪里。然后,你可以通過從同一目錄運行,以“可編輯”模式安裝程序包:
pip install -e .
它允許你更改源代碼(測試和應用程序)并隨意重新運行測試。這與運行類似,或者使用符號鏈接將你的包安裝到開發代碼中。python setup.py develop``conda develop
Python測試發現的約定
pytest
實現以下標準測試發現:
- 如果未指定參數,則從
testpaths
(如果已配置)或當前目錄開始收集。或者,命令行參數可以用于目錄,文件名或節點ID的任意組合。 - 遞歸到目錄,除非它們匹配
norecursedirs
。 - 在這些目錄,搜索
test_*.py
或*_test.py
文件,由他們進口的測試包名。 - 從這些文件中收集測試項目:
-
test
在課堂之外的前綴測試函數或方法 -
test
前綴測試Test
類中的前綴測試函數或方法(沒有__init__
方法)
-
有關如何自定義測試發現的示例更改標準(Python)測試發現。
在Python模塊中,pytest
還使用標準的unittest.TestCase子類化技術發現測試 。
選擇測試布局/導入規則
pytest
支持兩種常見的測試布局:
在應用程序代碼外測試
如果你有許多功能測試,或者出于其他原因希望將測試與實際應用程序代碼分開(通常是個好主意),那么將測試放入實際應用程序代碼之外的額外目錄可能會很有用:
setup.py
mypkg/
__init__.py
app.py
view.py
tests/
test_app.py
test_view.py
...
這有以下好處:
- 執行后,你的測試可以針對已安裝的版本運行。
pip install .
- 執行后,你可以使用可編輯安裝對本地副本運行測試。
pip install --editable .
- 如果你沒有
setup.py
文件并且依賴于默認情況下Python將當前目錄放入sys.path
以導入你的包,則可以執行直接對本地副本執行測試,而不使用。python -m pytest``pip
注意
有關調用和 調用之間差異的更多信息,請參閱pytest導入機制和sys.path / PYTHONPATH。pytest``python -m pytest
請注意,使用此方案時,你的測試文件必須具有唯一的名稱,因為pytest
將它們作為頂級模塊導入,因為 沒有包來從中獲取完整的包名稱。換句話說,在上面的示例中的試驗文件將被導入為test_app
和test_view
通過加入頂層模塊tests/
到 sys.path
。
如果需要具有相同名稱的測試模塊,可以將__init__.py
文件添加到 tests
文件夾和子文件夾,并將其更改為包:
setup.py
mypkg/
...
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
現在pytest將加載模塊,tests.foo.test_view
并tests.bar.test_view
允許你使用相同名稱的模塊。但是現在這引入了一個微妙的問題:為了從tests
目錄中加載測試模塊,pytest將存儲庫的根目錄sys.path
添加到 ,這增加了現在mypkg
也可導入的副作用。如果你使用像tox這樣的工具在虛擬環境中測試程序包,則會出現問題,因為你要測試程序包的已安裝版本,而不是存儲庫中的本地代碼。
在這種情況下,強烈建議使用src
應用程序根包位于根目錄的子目錄中的布局:
setup.py
src/
mypkg/
__init__.py
app.py
view.py
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
這種布局可以防止許多常見的陷阱,并且有很多好處,這在IonelCristianM?rie?的優秀博客文章中有更好的解釋 。
測試作為應用程序代碼的一部分
如果測試和應用程序模塊之間存在直接關系并希望將它們與應用程序一起分發,則將測試目錄內聯到應用程序包中非常有用:
setup.py
mypkg/
__init__.py
app.py
view.py
test/
__init__.py
test_app.py
test_view.py
...
在此方案中,使用以下--pyargs
選項可以輕松運行測試:
pytest --pyargs mypkg
pytest
將發現mypkg
安裝位置并從那里收集測試。
請注意,此布局也與src
上一節中提到的布局一起使用。
注意
你可以為你的應用程序使用Python3命名空間包(PEP420),但pytest仍將根據文件的存在執行測試包名稱發現__init__.py
。如果你使用上面兩個推薦的文件系統布局中的一個,但是__init__.py
從你的目錄中刪除它們,那么它應該適用于Python3.3及更高版本。但是,從“內聯測試”開始,你將需要使用絕對導入來獲取應用程序代碼。
注意
如果pytest
在遞歸到文件系統時找到“a / b / test_module.py”測試文件,它將確定導入名稱,如下所示:
- 確定
basedir
:這是第一個“向上”(朝向根)目錄,不包含__init__.py
。如果如兩者a
并b
包含一個__init__.py
文件,然后父目錄a
將成為basedir
。 - 執行以使測試模塊可以在完全限定的導入名稱下導入。
sys.path.insert(0, basedir)
-
import a.b.test_module
其中路徑是通過將路徑分隔符/
轉換為“。”字符來確定的。這意味著你必須遵循將目錄和文件名直接映射到導入名稱的約定。
這種有點進化的導入技術的原因在于,在較大的項目中,多個測試模塊可能相互導入,因此導出規范的導入名稱有助于避免出現意外情況,例如測試模塊導入兩次。
TOX
一旦完成了你的工作并希望確保你的實際軟件包通過所有測試,你可能需要查看tox,virtualenv測試自動化工具及其pytest支持。tox幫助你使用預定義的依賴項設置virtualenv環境,然后使用選項執行預配置的測試命令。它將針對已安裝的軟件包運行測試,而不是針對源代碼檢查,從而有助于檢測包裝故障。
與setuptools集成
你可以使用pytest-runner插件將測試運行集成到基于setuptools的項目中。
將此添加到setup.py
文件:
from setuptools import setup
setup(
# ...,
setup_requires=["pytest-runner", ...],
tests_require=["pytest", ...],
# ...,
)
并在setup.cfg
文件中創建一個別名:
[aliases]
test=pytest
如果你現在輸入:
python setup.py test
這將使用執行你的測試pytest-runner
。因為這是一個獨立版本,pytest
無需事先安裝,無論如何都需要調用test命令。你還可以使用其他參數傳遞給pytest,例如測試目錄或其他選項--addopts
。
你還可以setup.cfg
通過將文件放入以下[tool:pytest]
部分來指定文件中的其他pytest-ini選項:
[tool:pytest]
addopts = --verbose
python_files = testing/*/*.py
手動整合
如果由于某種原因你不想/不能使用pytest-runner
,你可以編寫自己的setuptools測試命令來調用pytest。
import sys
from setuptools.command.test import test as TestCommand
class PyTest(TestCommand):
user_options = [("pytest-args=", "a", "Arguments to pass to pytest")]
def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = ""
def run_tests(self):
import shlex
# import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(shlex.split(self.pytest_args))
sys.exit(errno)
setup(
# ...,
tests_require=["pytest"],
cmdclass={"pytest": PyTest},
)
現在,如果你運行:
python setup.py test
這將pytest
在需要時下載,然后按照你的預期運行測試。你可以使用--pytest-args
或-a
命令行選項傳遞單個參數字符串。例如:
python setup.py test -a "--durations=5"
相當于運行 pytest --durations=5