練習

已完成

在本練習中,您將使用 pytest 搭配 parametrize 來測試函式。 然後,您會更新測試類別來使用固件,而非 setup()teardown() 方法。 使用 parametrize 和使用固件可讓您在建立或更新測試時變得更有彈性。

步驟 1 - 在此練習中新增一個測試檔案

  1. 新建一個名為 test_advanced.py 的測試檔案並新增以下程式碼:

    def str_to_bool(string):
        if string.lower() in ['yes', 'y', '1']:
            return True
        elif string.lower() in ['no', 'n', '0']:
            return False
    
    

    函式 str_to_bool() 會接受字串做為輸入,並根據其內容傳回 TrueFalse 值。

  2. 請在相同檔案中附加 str_to_bool() 函式的測試。 使用 pytest.mark.parametrize() 先測試所有 true 值:

    import pytest 
    
    @pytest.mark.parametrize("string", ['Y', 'y', '1', 'YES'])
    def test_str_to_bool_true(string):
        assert str_to_bool(string) is True
    
  3. 接下來,使用 false 值附加另一個測試:

    @pytest.mark.parametrize("string", ['N', 'n', '0', 'NO'])
    def test_str_to_bool_false(string):
        assert str_to_bool(string) is False
    

    現在有兩項測試涵蓋 TrueFalse 傳回值的所有可能輸入。

注意

在與實際程式碼相同的檔案中執行測試並不常見。 為了操作簡單,本練習中的範例會在相同檔案內執行實際程式碼。 在實際的 Python 專案中,您會發現測試是以測試中程式碼的檔案和目錄作區隔。

步驟 2 - 執行測試和探索報告

在您新增測試之後,下一個步驟是執行 pytest 並檢查輸出。 使用增加的詳細程度旗標 (-v),您可以看到所有輸入值都會被視為個別的測試。

$ pytest -v test_avanced.py
============================= test session starts ==============================
Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 
rootdir: /private
collected 8 items

test_advanced.py::test_str_to_bool_true[Y] PASSED                        [ 12%]
test_advanced.py::test_str_to_bool_true[y] PASSED                        [ 25%]
test_advanced.py::test_str_to_bool_true[1] PASSED                        [ 37%]
test_advanced.py::test_str_to_bool_true[YES] PASSED                      [ 50%]
test_advanced.py::test_str_to_bool_false[N] PASSED                       [ 62%]
test_advanced.py::test_str_to_bool_false[n] PASSED                       [ 75%]
test_advanced.py::test_str_to_bool_false[0] PASSED                       [ 87%]
test_advanced.py::test_str_to_bool_false[NO] PASSED                      [100%]

============================== 8 passed in 0.01s ===============================

雖然您只撰寫了兩個測試函式,但由於 pytest 函式,parametrize() 總共能夠建立八個測試。

步驟 3 - 將現有的測試移植到固件

  1. 將新的類別型測試新增至 test_advanced.py 檔案。 此測試應該使用 setup()teardown() 函式來建立暫存檔,其中包含一些文字。 每次測試之後,就會移除檔案。 其看起來應該如下:

    import os
    
    
    class TestFile:
    
        def setup(self):
            with open("/tmp/done", 'w') as _f:
                _f.write("1")
    
        def teardown(self):
            try:
                os.remove("/tmp/done")
            except OSError:
                pass
    
        def test_done_file(self):
            with open("/tmp/done") as _f:
                contents = _f.read()
            assert contents == "1"
    

    此測試類別會建立一個檔案,但有問題,因為 /tmp/ 路徑並不保證存在於每個系統上。

  2. 建立一個固件,其會使用 pytesttmpdir() 裝置寫入至檔案並傳回路徑:

    import pytest
    
    @pytest.fixture
    def tmpfile(tmpdir):
        def write():
            file = tmpdir.join("done")
            file.write("1")
            return file.strpath
        return write
    

    tmpfile() 固件會使用 Pytest 的 tmpdir() 固件,其保證在測試完成之後會清除有效的暫存檔案。

  3. 更新類別 TestFile,使其使用固件而非協助程式方法:

    class TestFile:
    
        def test_f(self, tmpfile):
            path = tmpfile()
            with open(path) as _f:
                contents = _f.read()
            assert contents == "1"
    

    這個測試類別現在確保系統會建立一個暫存檔案,且此檔案具有適當的內容,讓判斷提示能夠運作。

檢查您的工作

現在,您應該會有名為 test_advanced.py 的 Python 檔案,其中包含下列程式碼:

  • 接受字串並視字串的內容傳回布林值的 str_to_bool() 函式。
  • str_to_bool() 函式的兩個參數化測試,一個測試 True 值,另一個測試 False 值。
  • 自訂 pytest 固件,其使用 tmpdir() 固件建立一個具有某些內容的暫存「完成」檔案。
  • 使用自訂 tmpfile() 固件來建立檔案的測試類別。

在終端機執行所有測試時,這些測試都應通過,沒有錯誤發生。