pytest 教學 一文,我們學會如何利用 pytest 更輕鬆地撰寫 Python 的測試程式,但 pytest 所提供的方便功能不僅僅如此,pytest 還有許多好用的 plugins 能夠利用,使用得當的話,將可增加開發效率。

剛好 15 amazing pytest plugins 中提到不少實用的 pytest plugins, 本文就將其中 7 項進行簡介。

本文環境

  • Python 3.7
  • pytest 6.2.3
  • macOS 11.2

本文所提及之 plugins 皆可透過 pip 進行安裝。

報表類 Plugins

pytest-sugar

pytest 預設測試報表大概像以下形式:

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item

test_sample.py F                                                     [100%]

================================= FAILURES =================================

透過安裝 pytest-sugar 可以將報表進一步美化,例如下圖(引用自官方文件):

是否變得較為賞心悅目呢?

pytest-spec

如果喜歡在測試中寫上詳細的規格(spec/specification) 不妨參考 pytest-spec , 該 plugin 可以讓我們將 spec 作為測試函數的名字,或者將 spec 記錄於 docstring 中,並且在測試結果報表中列印出來,例如下圖(引用自官方文件):

不過,該 plugin 並沒有詳述如何啟用該 plugin, 如果要啟用該 plugin, 須在執行 pytest 時加上 --spec 參數,例如:

$ pytest --spec

如果想進一步設定 pytest-spec, 可以在 pytest.ini 檔案中新增 [tool:pytest] 區塊,例如以下範例:

[tool:pytest]
spec_header_format = {module_path}:
spec_test_format = {result} {docstring_summary}
spec_success_indicator = ✓
spec_failure_indicator = ✗
spec_skipped_indicator = s
spec_indent = "  "

關於上述各項設定在此不多贅述,詳細請參考 官方文件

pytest-cov

測試時經常也會衡量覆蓋率,如果要在報表中顯示覆蓋率,可以安裝 pytest-cov

關於該 plugin 可以參考 Python pytest 整合 coverage 一文。

pytest-instafail

pytest 預設會在報表的最後才將失敗的測試報表一次列印出來,隨著測試越來越多,經常要等一段時間才能夠得到這些資訊。

為了解決上述困擾可以安裝 pytest-instafail ,並且於執行 pytest 時加上 --instafail , 一旦測試失敗就會馬上看到失敗的報告:

$ pytest --instafail

平行化測試 Plugins

pytest-xdist

隨著測試案例越來越多,測試的執行勢必需要一段時間處理,不過仍可以透過平行化測試將測試以多個 processes 分散處理,或者將這些測試透過網路送至其他伺服器進行處理,以加速測試的進行。

要達成平行化測試的話,能夠安裝 pytest-xdist 解決,最簡單的方式即是加上 -n <NUMCPUS> 參數指定平行成 n 個 processes 進行測試,一般都會指定與 CPU 個數相同的數字,例如以下指令以 4 個 processes 進行平行測試:

$ pytest -n 4

成功的話,可以發現報表多了 1 行 gw0 [3] / gw1 [3] / gw2 [3] / gw3 [3] ,代表目前有 4 個 processes 在處理測試:

========================= test session starts ==========================
platform darwin -- Python 3.7.0, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: ..., configfile: pytest.ini
plugins: instafail-0.4.2, spec-3.1.0, xdist-2.2.1, forked-1.3.0
gw0 [3] / gw1 [3] / gw2 [3] / gw3 [3]

若要透過網路將測試送至其他伺服器進行處理,請詳閱 官方文件

時間相關 Plugins

pytest-freezegun

如果測試是需要固定住時間點,使得不管怎麼呼叫 datetime.now() 都是該時間點的話,可以使用 pytest-freezegun

該 plugin 提供 1 個 fixture 稱為 freezer ,透過該 fixture 可以隨意固定時間點,例如:

import pytest

from datetime import datetime


def test_moving_date(freezer):
    now = datetime.now()
    freezer.move_to('2012-12-12')
    later = datetime.now()
    assert now == later

上述範例執行之後會失敗,可以從報表中發現 datetime object 都已經被取代為 FakeDatetime ,而且在執行 freezer.move_to('2012-12-12') 之後,呼叫 datetime.now() 其時間已經被固定為 2012-12-12 00:00:00 ,也因此 2 個 datetime.now() 並不相等:

>       assert now == later
E       assert FakeDatetime(...3, 36, 675724) == FakeDatetime(... 12, 12, 0, 0)
E         +FakeDatetime(2021, 4, 11, 16, 23, 36, 675724)
E         -FakeDatetime(2012, 12, 12, 0, 0)

當然 freezer.move_to() 函式除了日期也支援時間,例如 freezer.move_to('2012-12-12 12:12:12') ,其他更多用法可參閱官方文件

其他

pytest-picked

pytest-picked 可以只執行 git repo 中有更改的測試檔案,如此可以優先執行有變更的測試,以節省時間:

$ pytest --picked

官方文件有更詳細的用法,詳細可以至 pytest-picked 查看。

以上為 7 個實用的 pytest plugins 的介紹,如果想了解更多 pytest plugins 可參考 references 連結。

Happy Coding!

References

https://testandcode.com/116