自動化測試框架pytest教程8-配置文件

配置文件簡介

配置文件--那些影響pytest運行的非測試文件,可以節省時間和重複工作。如果你發現自己在測試中總是使用某些標誌,比如 --verbose 或 --strict-markers,你可以把它們藏在一個配置文件中,而不必總是輸入它們。除了配置文件,還有一些其他文件在使用pytest時很有用,可以使編寫和運行測試的工作更容易。我們將在本章中介紹所有這些文件。

瞭解 pytest 配置文件

  • pytest.ini

這是主要的pytest配置文件,允許你改變pytest的默認行爲。它的位置也定義了pytest的根目錄,或稱rootdir。

  • conftest.py

這個文件包含固定裝置和鉤子函數。它可以存在於rootdir或任何子目錄中。

  • init.py

當放在測試子目錄中時,這個文件允許你在多個測試目錄中擁有相同的測試文件名。

  • tox.ini, pyproject.toml, and setup.cfg:

這些文件可以代替 pytest.ini。如果你在項目中已經有這些文件之一,你可以用它來保存pytest的設置。

  • tox.ini

tox使用的,它是我們在第11章 tox和持續集成中看到的命令行自動測試工具。

  • pyproject.toml

用於打包 Python 項目,可以用來保存各種工具的設置,包括 pytest。

  • setup.cfg

也用於打包,並可以用來保存 pytest 的設置。

讓我們在一個項目目錄結構的例子中看看這些文件中的一些。

cards_proj
    ├── ... top level project files, src dir, docs, etc ...
    ├── pytest.ini
    └── tests
        ├── conftest.py
        ├── api
        │   ├── __init__.py
        │   ├── conftest.py
        │   └── ... test files for api ...
        └── cli
            ├── __init__.py
            ├── conftest.py
            └── ... test files for cli ...

在我們迄今爲止所使用的Cards項目的測試中,沒有測試目錄。然而,無論是開源還是閉源項目,測試通常存在於項目的測試目錄中。

在pytest.ini中保存設置和標誌

讓我們看看一個pytest.ini文件的例子。

ch8/project/pytest.ini

$ cat project/pytest.ini
;---
; Excerpted from "Python Testing with pytest, Second Edition",
; published by The Pragmatic Bookshelf.
; Copyrights apply to this code. It may not be used to create training material,
; courses, books, articles, and the like. Contact us if you are in doubt.
; We make no guarantees that this code is fit for any purpose.
; Visit https://pragprog.com/titles/bopytest2 for more book information.
;---
[pytest]
addopts =
    --strict-markers
    --strict-config
    -ra

testpaths = tests

markers =
    smoke: subset of tests
    exception: check for expected exceptions

該文件以[pytest]開頭,表示pytest設置的開始
addopts = --strict-markers --strict-config -ra

每行只允許一個標記。

這個例子是一個基本的pytest.ini文件,包括我幾乎總是設置的項目。讓我們簡單地瀏覽一下這些選項和設置。

addopts = --strict-markers --strict-config -ra

addopts設置使我們能夠列出我們一直想在這個項目中運行的pytest標誌。

--strict-markers告訴pytest對測試代碼中遇到的任何未註冊的標記提出錯誤,而不是警告。打開這個選項可以避免標記名稱的錯誤。

--strict-config告訴pytest在解析配置文件時遇到任何困難都會引發錯誤。默認是警告。打開這個選項可以避免配置文件中的錯誤不被注意。

-ra告訴pytest在測試運行結束時顯示額外的測試摘要信息。默認情況下,只顯示測試失敗和錯誤的額外信息。-ra的a部分告訴pytest顯示除通過測試以外的所有信息。這在失敗和錯誤的測試中增加了skipped, xfailed, 和 xpassed。

testpaths設置告訴pytest在哪裏尋找測試,如果你在命令行中沒有給出文件或目錄名稱。將testpaths設置爲test,告訴pytest在test目錄中尋找。

乍一看,將testpaths設置爲test似乎是多餘的,因爲pytest無論如何都會在那裏尋找,而且我們的src或docs目錄中沒有任何test_文件。然而,指定testpaths目錄可以節省一些啓動時間,特別是當我們的docs或src或其他目錄相當大時。

markers設置是用來聲明標記的,就像我們在《用自定義標記選擇測試》中做的那樣。

你可以在配置文件中指定更多的配置設置和命令行選項,你可以通過運行pytest --help看到所有的配置。

使用 tox.ini, pyproject.toml, 或 setup.cfg 來代替 pytest.ini

如果你爲一個已經有pyproject.toml, tox.ini, 或setup.cfg文件的項目編寫測試,你仍然可以使用pytest.ini來存儲你的pytest配置設置。或者你也可以將你的配置設置存儲在這些備用的配置文件中。這兩個非ini文件的語法有些不同,所以我們將分別看一下。

tox.ini文件包含了對tox的設置,這在第11章 tox和持續集成中會有更詳細的介紹。它也可以包括一個[pytest]部分。因爲它也是一個.ini文件,下面的tox.ini例子與前面的pytest.ini例子幾乎相同。唯一不同的是,也會有一個[tox]部分。

一個樣本的 tox.ini 文件看起來像這樣。

ch8/alt/tox.ini

;---
; Excerpted from "Python Testing with pytest, Second Edition",
; published by The Pragmatic Bookshelf.
; Copyrights apply to this code. It may not be used to create training material,
; courses, books, articles, and the like. Contact us if you are in doubt.
; We make no guarantees that this code is fit for any purpose.
; Visit https://pragprog.com/titles/bopytest2 for more book information.
;---
[tox]
; tox specific settings

[pytest]
addopts =
    --strict-markers
    --strict-config
    -ra

testpaths = tests

markers =
    smoke: subset of tests
    exception: check for expected exceptions

pyproject.toml 文件開始是一個用於打包 Python 項目的文件;然而,Poetry 和 Flit項目使用 pyproject.toml 來定義項目設置。Setuptools 庫,在 Flit 和 Poetry 出現之前一直是標準的打包工具,傳統上沒有使用 pyproject.toml。然而,現在你可以使用Setuptools與pyproject.toml。2018年,一個名爲Black的Python代碼格式化器開始流行起來。配置Black的唯一方法是使用pyproject.toml。從那時起,越來越多的工具開始支持將配置存儲在 pyproject.toml 中,包括 pytest。

因爲TOML是一個不同於.ini文件的配置文件標準,其格式有些不同,但相當容易習慣。格式看起來像這樣。

ch8/alt/pyproject.toml

[tool.pytest.ini_options]
addopts = [
    "--strict-markers",
    "--strict-config",
    "-ra"
    ]

testpaths = "tests"

markers = [
        "smoke: subset of tests",
        "exception: check for expected exceptions"
]

用[tool.pytest.ini_options]代替[pytest]來開始這一部分。設置值需要在其周圍加上引號,設置值的列表需要在括號中列出字符串。

setup.cfg文件的格式更像.ini。下面是我們的配置例子中setup.cfg文件的樣子。

ch8/alt/setup.cfg

[tool:pytest]
addopts =
    --strict-markers
    --strict-config
    -ra

testpaths = tests

markers =
    smoke: subset of tests
    exception: check for expected exceptions

這裏,與pytest.ini之間唯一明顯的區別是[tool:pytest]的部分指定符。

然而,pytest文檔警告說,.cfg解析器與.ini文件解析器不同,這種不同可能會導致難以追蹤的問題。

確定根目錄和配置文件

在開始尋找要運行的測試文件之前,pytest就會讀取配置文件--pytest.ini文件或包含pytest部分的tox.ini、setup.cfg、或pyproject.toml文件。

如果你輸入了一個測試目錄,pytest就開始在那裏尋找。如果你輸入了多個文件或目錄,pytest就從所有這些文件或目錄的共同祖先開始。如果你沒有傳入文件或目錄,它就從當前目錄開始。如果pytest在起始目錄下找到配置文件,那就是根目錄。如果沒有,pytest就沿着目錄樹往上走,直到找到有pytest部分的配置文件。一旦pytest找到配置文件,它就會把找到它的目錄標記爲根目錄,或者叫rootdir。這個根目錄也是測試節點ID的相對根。它還告訴你它在哪裏找到了一個配置文件。

圍繞使用哪個配置文件和根目錄在哪裏的規則,一開始似乎很混亂。然而,有一個定義明確的根目錄搜索過程,並讓pytest顯示根目錄是什麼,可以讓我們在不同級別上運行測試,並保證pytest會找到正確的配置文件。例如,即使你改變目錄進入測試目錄深處的測試子目錄,pytest仍然會在項目的頂部找到你的配置文件。

即使你不需要任何配置設置,在項目的頂部放置空的pytest.ini仍然是一個好主意。如果你沒有任何配置文件,pytest會一直搜索到你的文件系統的根。在最好的情況下,這隻會在pytest尋找的時候造成一點延遲。最壞的情況是,它將在途中找到一個與你的項目無關的文件。

一旦找到了一個配置文件,pytest就會在測試運行的頂部打印出它所使用的rootdir和配置文件。

============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0
rootdir: D:\code\pytest_quick\ch8\project, configfile: pytest.ini, testpaths: tests
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0
collected 28 items

tests\api\test_add.py .....                                              [ 17%]
tests\api\test_config.py .                                               [ 21%]
tests\api\test_count.py ...                                              [ 32%]
tests\api\test_delete.py ...                                             [ 42%]
tests\api\test_finish.py ....                                            [ 57%]
tests\api\test_list.py ..                                                [ 64%]
tests\api\test_path.py .                                                 [ 67%]
tests\api\test_start.py ....                                             [ 82%]
tests\api\test_update.py ....                                            [ 96%]
tests\api\test_version.py .                                              [100%]

============================= 28 passed in 0.71s ==============================

如果你設置了測試路徑,它還會顯示測試路徑,我們也設置了。這很好。

用conftest.py共享本地固定裝置和鉤子函數

conftest.py文件用於存儲固定裝置和鉤子函數。你可以在一個項目中擁有任意多的 conftest.py 文件,甚至每個測試子目錄下都有一個。在 conftest.py 文件中定義的任何內容都適用於該目錄和所有子目錄中的測試。

如果你在測試層有一個頂級的conftest.py文件,在那裏定義的固定程序可以用於頂級測試目錄及以下的所有測試。然後,如果有特定的夾具只適用於子目錄,它們可以被定義在該子目錄下的另一個conftest.py文件中。例如,GUI測試可能需要與API測試不同的固定裝置,它們也可能想共享一些。

然而,最好是堅持使用一個conftest.py文件,這樣你就可以很容易地找到夾具定義。儘管你總是可以通過使用 pytest --fixtures -v 找到夾具的定義,但如果你知道它在你正在看的測試文件中,或者在另一個文件中,即 conftest.py 文件中,還是會更容易。

避免測試文件名的衝突

init.py 文件對 pytest 的影響只有:它允許你有重複的測試文件名。

如果你在每個測試子目錄下都有init.py文件,你可以讓同一個測試文件名出現在多個目錄下。就是這樣--擁有 init.py 文件的唯一原因。

下面是一個例子。

$ cd /path/to/code/ch8/dup
$ tree tests_with_init
    tests_with_init
    ├── api
    │   ├── __init__.py
    │   └── test_add.py
    ├── cli
    │   ├── __init__.py
    │   └── test_add.py
    └── pytest.ini

我們可能想通過API和CLI來測試一些添加功能,所以在這兩個地方都有一個test_add.py似乎很合理。

只要我們在api和cli目錄下都有一個init.py文件,這個測試就能順利進行。

$ pytest -v tests_with_init
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
rootdir: D:\code\pytest_quick\ch8\dup\tests_with_init, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0
collecting ... collected 2 items

tests_with_init\api\test_add.py::test_add PASSED                         [ 50%]
tests_with_init\cli\test_add.py::test_add PASSED                         [100%]

============================== 2 passed in 0.09s ==============================
$ pytest -v tests_no_init
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
rootdir: D:\code\pytest_quick\ch8\dup\tests_no_init, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0
collecting ... collected 1 item / 1 error

=================================== ERRORS ====================================
______________________ ERROR collecting cli/test_add.py _______________________
import file mismatch:
imported module 'test_add' has this __file__ attribute:
  D:\code\pytest_quick\ch8\dup\tests_no_init\api\test_add.py
which is not the same as the test file we want to collect:
  D:\code\pytest_quick\ch8\dup\tests_no_init\cli\test_add.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
=========================== short test summary info ===========================
ERROR tests_no_init\cli\test_add.py
!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
============================== 1 error in 0.10s ===============================

然而,如果我們省去init.py文件,它就不會工作。這裏是同一個目錄,沒有init.py文件。

錯誤信息強調我們有兩個名字相同的文件,並建議改變文件名。改變文件名可以避免這個錯誤,但你也可以添加init.py文件,讓它們保持原樣。

當你遇到重複的文件名時,是一個如此令人困惑的錯誤,所以把init.py文件放在子目錄中就可以了,這是一個不錯的習慣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章