Resúmenes
Apuntes universitarios en formato Markdown desde Obsidian.
Test avanzati per Python
download Descargar MDLa descrizione di un dominio applicativo è volutamente minimale in modo da poterci concentrare sui problemi architettonici e su come risolverli.
Come spinto dal modello di architettura pulita, siamo interessati a separare i diversi livelli del sistema: ricorda che ci sono diversi modi per implementare i concetti di architettura pulita e il codice che puoi inventare dipende fortemente da ciò che la tua lingua preferita ti consente di fare.
Moduli di terze parti
Un approccio semplice per convalidare tipi e valori può essere l'utilizzo di istruzioni IF e la generazione di eccezioni. Riutilizziamo meglio i moduli di terze parti e le nuove funzionalità di Python. I suggerimenti sul tipo possono essere utilizzati da un analizzatore statico. Le classi di dati sono utili per definire le classi dalle annotazioni.
@typechecked e @dataclass
Il comando @typechecked applica la convalida del tipo a tutti i metodi con suggerimenti sul tipo.
Il comando @dataclass (con i parametri frozen=True e order=True) genera init, eq e altri metodi, inibisce le modifiche, genera lt e altri metodi.
Struttura del progetto

Facendo riferimento all'immagine qui sopra, possiamo creare una struttura per il nostro progetto:
- Moduli di progetto: (package dealer) classi di dominio, classi di menu generiche (potrebbe essere un modulo di terze parti), classi di I/O dell'app;
- Moduli di test: (package tests) test qui, la cartella riflette la struttura di tutte le altre cartelle e moduli nel progetto Un file test_ per ogni modulo del progetto;
- Modulo delle utility: (package validation) utilità per facilitare la convalida (potrebbe essere un modulo di terze parti);
- Modulo root: i dati vengono caricati e salvati automaticamente da questo file.
Consigli per costruttore e metodi per il modulo di test
Fornire metodi di analisi per primitive di dominio che non siano stringhe.
create_key è un argomento aggiuntivo di init (e __post_init). *create_key è una variabile di classe privata (in realtà, il nome è randomizzato). Facciamo finta di avere create_key è uguale a __create_key*: ecco come creiamo un costruttore privato in Python.
Utilizzare una stringa come suggerimento sul tipo se il tipo non è ancora completamente definito. Fallo per i metodi che restituiscono istanze della classe: se lo fai da qualche altra parte, probabilmente hai dipendenze circolari. Un metodo con intestazione @property è un metodo autonomo a cui vogliamo accedere senza parentesi.
Fixtures
Possiamo definire dispositivi per oggetti che vogliamo utilizzare in molti test. Ad esempio:
@pytest.fixture
def cars():
return [
Car(Plate('AB123CD'), Producer('Car Producer'), Model('Model'), Price.create(100)),
Car(Plate('AB123CE'), Producer('Car Producer'), Model('Model'), Price.create(11000)),
Car(Plate('AB123CF'), Producer('Car Producer'), Model('Model'), Price.create(21000)),
]
Bisogna passare il nome della fixtures come argomento per accedere all'oggetto restituito dall'apparecchiatura, come per esempio:
def test_car_type_is_car(cars):
assert cars[0].type == 'Car'
Test delle funzionalità
La classe principale deve fornire tutte le funzionalità, ma non deve preoccuparsi delle operazioni di I/O. Testa tutte le funzionalità tramite TDD tra cui test prima, codice dopo: non è una religione, va bene anche testare e codificare in parallelo.
Field
Prendiamo in esame questa proprietà:
__vehicles: List[Union[Car, Moto]] = field(default-factor=list, init=False)
default_factory è un metodo da chiamare per creare il valore predefinito. Se init è falso, si esclude questo campo da init.
Mock
Dobbiamo simulare una chiamata e verificare che la chiamata sia stata effettivamente completata. In Python questo è solitamente fatto da un mock. Mock è un oggetto su cui possono essere chiamati essenzialmente tutti i metodi. Le chiamate vengono registrate e possono essere verificate in seguito. Un esempio di utilizzo è il seguente:
def test_entry_on_selected():
mocked_on_selected = Mock()
entry = Entry(Key('1'), Description('Say hi'), on_selected=lambda: mocked_on_selected())
entry.on_selected()
mocked_on_selected.assert_called_once()
Usiamo il metodo call dunder, potremmo anche usare mocked_on_select.foo(): verifica che il metodo simulato sia stato effettivamente chiamato. Possiamo anche controllare gli argomenti delle chiamate.
Possiamo anche prendere in giro oggetti globali, oggetti dichiarati da qualche altra parte. A questo scopo utilizziamo le patch come di seguito:
@patch('builtins.print')
def test_entry_on_selected_print_something(mocked_print):
entry = Entry(Key('1'), Description('Say hi'), on_selected=lambda: print('hi'))
entry.on_selected()
assert mocked_print.mock_calls == [call('hi')]
Vorremmo stampare qualcosa quando selezionato: verifichiamo che print sia stato effettivamente chiamato con l'argomento 'hi'.
Usa side_effect per elencare i valori di ritorno di un oggetto patchato.
I metodi privati sono utili per garantire che una classe non venga utilizzata in modo improprio, ma possono anche rendere più difficili i test: raggiungere tutti i percorsi può essere difficile.
Fare affidamento su oggetti globali, come input() e print(), semplifica il codice, ma può anche rendere più difficili i test: abbiamo bisogno di mock e patch.
Coverage
Ogni metrica può essere ingannata, non ingannare te stesso. Utilizza l'analisi della coverage per identificare i test mancanti e il codice irraggiungibile.