如何擷取 stdout/stderr 輸出

預設 stdout/stderr/stdin 擷取行為

在測試執行期間,傳送至 stdoutstderr 的任何輸出都會被擷取。如果測試或設定方法失敗,其對應的擷取輸出通常會與失敗追蹤一起顯示。(此行為可透過 --show-capture 命令列選項進行設定)。

此外,stdin 會設定為「空值」物件,當嘗試從中讀取時會失敗,因為在執行自動化測試時很少需要等待互動式輸入。

預設情況下,擷取是透過攔截寫入至低階檔案描述詞來完成的。這允許從簡單列印陳述式以及測試啟動的子處理序擷取輸出。

設定擷取方法或停用擷取

有下列三種方式讓 pytest 執行擷取

  • fd(檔案描述詞)層級擷取(預設):傳送至作業系統檔案描述詞 1 和 2 的所有寫入都會被擷取。

  • sys 層級擷取:只有寫入 Python 檔案 sys.stdoutsys.stderr 的內容會被擷取。不會擷取寫入檔案描述詞的內容。

  • tee-sys 擷取:寫入 Python 檔案 sys.stdoutsys.stderr 的內容會被擷取,但寫入內容也會傳遞至實際的 sys.stdoutsys.stderr。這允許輸出「即時列印」並擷取以供外掛程式使用,例如 junitxml(pytest 5.4 中的新功能)。

你可以從命令列影響輸出擷取機制

pytest -s                  # disable all capturing
pytest --capture=sys       # replace sys.stdout/stderr with in-mem files
pytest --capture=fd        # also point filedescriptors 1 and 2 to temp file
pytest --capture=tee-sys   # combines 'sys' and '-s', capturing sys.stdout/stderr
                           # and passing it along to the actual sys.stdout/stderr

使用列印陳述式進行除錯

預設擷取 stdout/stderr 輸出的主要好處之一是你可以使用列印陳述式進行除錯

# content of test_module.py


def setup_function(function):
    print("setting up", function)


def test_func1():
    assert True


def test_func2():
    assert False

執行此模組將精確顯示失敗函式的輸出並隱藏其他函式

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items

test_module.py .F                                                    [100%]

================================= FAILURES =================================
________________________________ test_func2 ________________________________

    def test_func2():
>       assert False
E       assert False

test_module.py:12: AssertionError
-------------------------- Captured stdout setup ---------------------------
setting up <function test_func2 at 0xdeadbeef0001>
========================= short test summary info ==========================
FAILED test_module.py::test_func2 - assert False
======================= 1 failed, 1 passed in 0.12s ========================

從測試函式存取擷取的輸出

capsyscapsysbinarycapfdcapfdbinary 固定裝置允許存取測試執行期間建立的 stdout/stderr 輸出。以下是執行一些輸出相關檢查的測試函式範例

def test_myoutput(capsys):  # or use "capfd" for fd-level
    print("hello")
    sys.stderr.write("world\n")
    captured = capsys.readouterr()
    assert captured.out == "hello\n"
    assert captured.err == "world\n"
    print("next")
    captured = capsys.readouterr()
    assert captured.out == "next\n"

readouterr() 呼叫會擷取目前為止的輸出,而且會持續擷取。測試函式完成後,原始串流將會復原。使用 capsys 這種方式可以讓你的測試不必在意設定/重設輸出串流,而且也能與 pytest 自身的每個測試擷取良好互動。

如果你想在檔案描述符層級擷取,你可以使用 capfd 固定裝置,它提供完全相同的介面,但允許從直接寫入作業系統層級輸出串流 (FD1 和 FD2) 的函式庫或子程序擷取輸出。

readouterr 的傳回值已變更為具有兩個屬性的 namedtupleouterr

如果測試中的程式碼寫入非文字資料,你可以使用 capsysbinary 固定裝置擷取,它會從 readouterr 方法傳回 bytes

如果測試中的程式碼寫入非文字資料,你可以使用 capfdbinary 固定裝置擷取,它會從 readouterr 方法傳回 bytescapfdbinary 固定裝置在檔案描述符層級執行。

若要暫時停用測試中的擷取,capsyscapfd 都有一個 disabled() 方法,可用作內容管理員,停用 with 區塊內的擷取

def test_disabling_capturing(capsys):
    print("this output is captured")
    with capsys.disabled():
        print("output not captured, going directly to sys.stdout")
    print("this output is also captured")