Esercitazione 9 - Tempi di verifica

La maggior parte dello sviluppo del software non comporta la scrittura di nuovo codice, ma la modifica di quello esistente. Assicurarsi che il codice esistente continui a funzionare nel modo in cui ci aspettiamo è una parte fondamentale del processo di sviluppo del software. Un modo per garantire il comportamento della nostra applicazione è una serie di test.

Esecuzione della suite di test

Si scopre che il nostro progetto ha già una suite di test! Quando abbiamo generato il nostro progetto, sono state generate due directory di primo livello: src e tests. La cartella src contiene il codice della nostra applicazione; la cartella tests contiene la nostra suite di test. All’interno della cartella tests c’è un file chiamato test_app.py con il seguente contenuto:

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

Questo è un Pytest caso di test - un blocco di codice che può essere eseguito per verificare un comportamento dell’applicazione. In questo caso, il test è un segnaposto e non verifica nulla della nostra applicazione, ma è un test che possiamo eseguire.

Possiamo eseguire questa suite di test usando l’opzione --test di briefcase dev. Poiché è la prima volta che si eseguono dei test, è necessario inserire anche l’opzione -r per assicurarsi che anche i requisiti dei test siano installati:

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

Successo! Abbiamo appena eseguito un singolo test che verifica che la matematica di Python funziona nel modo previsto (che sollievo!).

Sostituiamo questo test segnaposto con un test per verificare che il nostro metodo greeting() si comporti come ci aspettiamo. Sostituire il contenuto di test_app.py con il seguente:

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"

Questo definisce due nuovi test, che verificano i due comportamenti che ci aspettiamo di vedere: l’output quando viene fornito un nome e l’output quando il nome è vuoto.

Ora possiamo eseguire nuovamente la suite di test. Questa volta non è necessario fornire l’opzione -r, poiché i requisiti per i test sono già stati installati; è sufficiente usare l’opzione --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 ===============================

Eccellente! Il nostro metodo di utilità greeting() funziona come previsto.

Sviluppo guidato dai test

Ora che abbiamo una suite di test, possiamo usarla per guidare lo sviluppo di nuove funzionalità. Modifichiamo la nostra applicazione per avere un saluto speciale per un utente in particolare. Possiamo iniziare aggiungendo un caso di test per il nuovo comportamento che vorremmo vedere in fondo a test_app.py:

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

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

Quindi, eseguire la suite di test con questo nuovo test:

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

Questa volta, vediamo un fallimento del test e l’output spiega la fonte del fallimento: il test si aspetta l’output «BeeWare the IDEs of Python!», ma la nostra implementazione di greeting() restituisce «Hello, Brutus». Modifichiamo l’implementazione di greeting() in src/helloworld/app.py per avere il nuovo comportamento:

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

Se eseguiamo nuovamente i test, vedremo che i nostri test passano:

(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 di runtime

Finora abbiamo eseguito i test in modalità di sviluppo. Questo è particolarmente utile quando si sviluppano nuove funzionalità, in quanto si può iterare rapidamente sull’aggiunta di test e sull’aggiunta di codice per far sì che tali test passino. Tuttavia, a un certo punto, si vorrà verificare che il codice venga eseguito correttamente anche nell’ambiente dell’applicazione bundle.

Le opzioni --test e -r possono essere passate anche al comando run. Se si usa briefcase run --test -r, verrà eseguita la stessa suite di test, ma all’interno del bundle dell’applicazione confezionata e non nell’ambiente di sviluppo:

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

Come per briefcase dev --test, l’opzione -r è necessaria solo la prima volta che si esegue la suite di test, per assicurarsi che le dipendenze del test siano presenti. Nelle esecuzioni successive, si può omettere questa opzione.

Si può anche usare l’opzione --test sui backend mobili: - quindi briefcase run iOS --test e briefcase run android --test funzioneranno entrambi, eseguendo la suite di test sul dispositivo mobile selezionato.

Prossimi passi

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…