教學 9 - 測試時間

大多數軟體開發並不涉及編寫新程式碼,而是修改現有程式碼。確保現有程式碼繼續按照我們期望的方式運作是軟體開發過程的關鍵部分。確保我們的應用程式行為的一種方法是使用 測試套件

運行測試套件

事實上,我們的專案已經有測試套件了!當我們最初產生專案時,產生了兩個資料夾: srctestssrc 資料夾包含我們應用程式的程式碼; tests 資料夾包含我們的測試套件。在 tests 資料夾內有一個名為 test_app.py 的文件,其中包含以下內容:

def test_first():
    "An initial test for the app"
    assert 1 + 1 == 2

這是一個 Pytest測試範例 - 可以執行以驗證應用程式的某些行為的程式碼區塊。在本例中,測試僅是一個範例,不會測試有關我們應用程式的任何內容 - 但它是我們可以執行的測試。

我們可以使用 briefcase dev--test 選項來執行這個測試套件。由於這是我們第一次執行測試,我們還需要傳入 -r 選項以確保測試要求也已安裝:

(beeware-venv) $ briefcase dev --test -r

[helloworld] Installing requirements...
...
Installing dev requirements... done

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
platform darwin -- Python 3.11.0, pytest-7.2.0, pluggy-1.0.0 -- /Users/brutus/beeware-tutorial/beeware-venv/bin/python3.11
cachedir: /var/folders/b_/khqk71xd45d049kxc_59ltp80000gn/T/.pytest_cache
rootdir: /Users/brutus
plugins: anyio-3.6.2
collecting ... collected 1 item

tests/test_app.py::test_first PASSED                                     [100%]

============================== 1 passed in 0.01s ===============================

成功了!我們剛剛執行了一個測試,證實了 Python 的數學按照我們期望的方式工作(真是鬆了一口氣!)。

讓我們用一個測試來取代這個範例,以驗證我們的 greeting() 方法的行為是否符合我們的預期。將 test_app.py 的內容替換為以下內容:

from helloworld.app import greeting


def test_name():
    """If a name is provided, the greeting includes the name"""

    assert greeting("Alice") == "Hello, Alice"


def test_empty():
    """If a name is not provided, a generic greeting is provided"""

    assert greeting("") == "Hello, stranger"

這定義了兩個新的測試,驗證我們期望看到的兩個行為:提供名稱時的輸出,以及名稱為空時的輸出。

我們現在可以重新運行測試套件。這次,我們不需要提供 -r 選項,因為測試要求已經安裝了;我們只需要使用 --test 選項:

(beeware-venv) $ briefcase dev --test

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
...
collecting ... collected 2 items

tests/test_app.py::test_name PASSED                                      [ 50%]
tests/test_app.py::test_empty PASSED                                     [100%]

============================== 2 passed in 0.11s ===============================

非常好!我們的 greeting() 實用方法按預期工作。

用測試來驅動開發

現在我們有了測試套件,我們可以用它來驅動新功能的開發。讓我們修改我們的應用程序,為某個特定用戶提供特殊的問候語。我們可以先為我們希望在 test_app.py 底部看到的新行為新增一個測試案例:

def test_brutus():
    """If the name is Brutus, a special greeting is provided"""

    assert greeting("Brutus") == "BeeWare the IDEs of Python!"

然後,使用這個新測試來執行測試套件:

(beeware-venv) $ briefcase dev --test

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
...
collecting ... collected 3 items

tests/test_app.py::test_name PASSED                                      [ 33%]
tests/test_app.py::test_empty PASSED                                     [ 66%]
tests/test_app.py::test_brutus FAILED                                    [100%]

=================================== FAILURES ===================================
_________________________________ test_brutus __________________________________

    def test_brutus():
        """If the name is Brutus, a special greeting is provided"""

>       assert greeting("Brutus") == "BeeWare the IDEs of Python!"
E       AssertionError: assert 'Hello, Brutus' == 'BeeWare the IDEs of Python!'
E         - BeeWare the IDEs of Python!
E         + Hello, Brutus

tests/test_app.py:19: AssertionError
=========================== short test summary info ============================
FAILED tests/test_app.py::test_brutus - AssertionError: assert 'Hello, Brutus...
========================= 1 failed, 2 passed in 0.14s ==========================

這次,我們看到測試失敗 - 輸出解釋了失敗的根源:測試期望輸出 BeeWare the IDEs of Python! ,但我們的 greeting() 實現返回 Hello, Brutus 。讓我們修改 src/helloworld/app.py 中的 greeting() 實作以獲得新的行為:

def greeting(name):
    if name:
        if name == "Brutus":
            return "BeeWare the IDEs of Python!"
        else:
            return f"Hello, {name}"
    else:
        return "Hello, stranger"

如果我們再次運行測試,我們現在將看到測試通過:

(beeware-venv) $ briefcase dev --test

[helloworld] Running test suite in dev environment...
===========================================================================
============================= test session starts ==============================
...
collecting ... collected 3 items

tests/test_app.py::test_name PASSED                                      [ 33%]
tests/test_app.py::test_empty PASSED                                     [ 66%]
tests/test_app.py::test_brutus PASSED                                    [100%]

============================== 3 passed in 0.15s ===============================

運行時測試

到目前為止,我們一直在開發模式下執行測試。當您開發新功能時,這特別有用,因為您可以快速迭代添加測試,並添加程式碼以使這些測試通過。但是,在某些時候,您需要驗證您的程式碼在部署的環境中是否也可以正確運行。

--test-r 選項也可以傳遞給 run 指令。如果您使用 briefcase run --test -r ,將運行相同的測試套件,但它將在部署的環境中運行,而不是在您的開發環境中運行:

(beeware-venv) $ briefcase run --test -r

[helloworld] Updating application code...
Installing src/helloworld... done
Installing tests... done

[helloworld] Updating requirements...
...
[helloworld] Built build/helloworld/macos/app/Hello World.app (test mode)

[helloworld] Starting test suite...
===========================================================================
Configuring isolated Python...
Pre-initializing Python runtime...
PythonHome: /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib
PYTHONPATH:
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python311.zip
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib/lib-dynload
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/app_packages
- /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/app
Configure argc/argv...
Initializing Python runtime...
Installing Python NSLog handler...
Running app module: tests.helloworld
---------------------------------------------------------------------------
============================= test session starts ==============================
...
collecting ... collected 3 items

tests/test_app.py::test_name PASSED [ 33%]
tests/test_app.py::test_empty PASSED [ 66%]
tests/test_app.py::test_brutus PASSED [100%]

============================== 3 passed in 0.21s ===============================

[helloworld] Test suite passed!

briefcase dev --test 一樣,僅在第一次執行測試套件時才需要 -r 選項,以確保測試依賴項存在。在後續運行中,您可以忽略此選項。

您還可以在移動環境下使用 --test 選項: - 因此 briefcase run iOS --testbriefcase run android --test 都可以工作,在您選擇的移動設備上運行測試套件。

下一步

We’ve now got a a test suite for our application. But it still looks like a tutorial app. Is there anything we can do about that? Turn to Tutorial 10 to find out…