Tutorial 9 - Prüfzeiten

Bei der Softwareentwicklung wird meist kein neuer Code geschrieben, sondern bestehender Code geändert. Die Sicherstellung, dass vorhandener Code weiterhin so funktioniert, wie wir es erwarten, ist ein wichtiger Teil des Softwareentwicklungsprozesses. Eine Möglichkeit, das Verhalten unserer Anwendung sicherzustellen, ist eine Testsuite.

Ausführen der Testsuite

Es stellt sich heraus, dass unser Projekt bereits eine Testsuite hat! Als wir unser Projekt ursprünglich erzeugten, wurden zwei Verzeichnisse der obersten Ebene erzeugt: src und tests. Der Ordner src enthält den Code für unsere Anwendung; der Ordner tests enthält unsere Testsuite. Innerhalb des Ordners tests befindet sich eine Datei namens test_app.py mit folgendem Inhalt:

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

Dies ist ein Pytest Testfall - ein Codeblock, der ausgeführt werden kann, um ein bestimmtes Verhalten Ihrer Anwendung zu überprüfen. In diesem Fall ist der Test ein Platzhalter und testet nichts über unsere Anwendung - aber es ist ein Test, den wir durchführen können.

Wir können diese Testsuite mit der Option --test für briefcase dev ausführen. Da dies das erste Mal ist, dass wir Tests ausführen, müssen wir auch die Option -r übergeben, um sicherzustellen, dass die Testanforderungen auch installiert werden:

(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 ===============================

Erfolgreich! Wir haben soeben einen einzigen Test durchgeführt, der bestätigt, dass die Python-Mathematik so funktioniert, wie wir es erwartet haben (was für eine Erleichterung!).

Lassen Sie uns diesen Platzhaltertest durch einen Test ersetzen, der überprüft, ob sich unsere Methode Gruß() so verhält, wie wir es erwarten. Ersetzen Sie den Inhalt von test_app.py durch den folgenden:

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"

Dies definiert zwei neue Tests, die beiden erwarteten Verhaltensweisen überprüfen: die Ausgabe, wenn ein Name angegeben wird, und die Ausgabe, wenn der Name leer ist.

Wir können nun die Testsuite erneut ausführen. Diesmal brauchen wir die Option -r nicht, da die Testanforderungen bereits installiert wurden; wir müssen nur die Option --test verwenden:

(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 ===============================

Ausgezeichnet! Unsere Utility-Methode Gruß() funktioniert wie erwartet.

Testgetriebene Entwicklung

Jetzt, da wir eine Test-Suite haben, können wir sie nutzen, um die Entwicklung neuer Funktionen voranzutreiben. Ändern wir unsere Anwendung, um eine spezielle Begrüßung für einen bestimmten Benutzer zu erhalten. Wir können damit beginnen, indem wir einen Testfall für das neue Verhalten, das wir gerne sehen möchten, am Ende von test_app.py hinzufügen:

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

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

Führen Sie dann die Testsuite mit diesem neuen Test aus:

(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 ==========================

Dieses Mal sehen wir einen Testfehler - und die Ausgabe erklärt die Quelle des Fehlers: der Test erwartet die Ausgabe „BeeWare the IDEs of Python!“, aber unsere Implementierung von Gruß() gibt „Hallo, Brutus“ zurück. Ändern wir die Implementierung von Gruß() in src/helloworld/app.py, um das neue Verhalten zu erhalten:

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

Wenn wir die Tests noch einmal durchführen, sehen wir, dass die Tests bestanden wurden:

(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 ===============================

Laufzeittests

Bislang haben wir die Tests im Entwicklungsmodus ausgeführt. Dies ist besonders nützlich, wenn Sie neue Funktionen entwickeln, da Sie schnell Tests und Code hinzufügen können, damit diese Tests erfolgreich sind. Irgendwann werden Sie jedoch sicherstellen wollen, dass Ihr Code auch als Anwendungspaket korrekt ausgeführt wird.

Die Optionen --test und -r können auch an den Befehl run übergeben werden. Wenn Sie briefcase run --test -r verwenden, wird die gleiche Testsuite ausgeführt, allerdings innerhalb des Anwendungspakets und nicht in Ihrer Entwicklungsumgebung:

(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!

Wie bei briefcase dev --test wird die Option -r nur bei der ersten Ausführung der Testsuite benötigt, um sicherzustellen, dass die Testabhängigkeiten vorhanden sind. Bei späteren Durchläufen können Sie diese Option weglassen.

Sie können auch die Option --test auf mobilen Backends verwenden: - so funktionieren briefcase run iOS --test und briefcase run android --test, die Testsuite auf dem von Ihnen ausgewählten mobilen Gerät ausführen.

Nächste Schritte

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…