Installare pytest e il tuo primo test
Perché i controlli manuali smettono di funzionare, perché un semplice assert non dice quasi nulla e come installare pytest, scrivere ed eseguire un primo file di test. Inclusi i punti, la riga di riepilogo, -q, -v e i codici di uscita.
Come vuoi imparare?
Il problema che stiamo risolvendo
Ecco una piccola funzione, e il modo in cui la maggior parte di noi controlla una funzione simile — eseguirla e guardare cosa restituisce:
def discounted(price, percent):
return price - price * percent / 100
print(discounted(200, 10))
print(discounted(80, 25))
print(discounted(99, 0))180.0
60.0
99.0Leggi i tre numeri, fai i calcoli a mente e decidi che sono corretti. Questo è un test. È un test manuale, e presenta due problemi.
Il primo è che non scala. Tre numeri sono facili da verificare a occhio. Trenta funzioni con cinque casi ciascuna, ricontrollate dopo ogni modifica, non lo sono — e nessuno lo fa.
Il secondo è peggiore. Un mese dopo qualcuno fa pulizia nella funzione:
def discounted(price, percent):
return price * (1 - percent // 100)$ python -c "from prices import discounted; print(discounted(200, 10))"
200Sembra ragionevole, e viene eseguita senza errori. Ma // è una divisione intera, quindi 10 // 100 fa 0, e ogni sconto è sparito silenziosamente. Il codice che prima funzionava e ora non funziona più si chiama regressione, e una regressione è esattamente ciò che sfugge ai controlli manuali — perché nessuno torna indietro a ricontrollare i vecchi numeri.
La soluzione consiste nello scrivere le risposte attese nel codice, in modo che una macchina possa controllarle ogni volta. Python ha già un'istruzione per questo: assert. Inserisci i controlli in un file chiamato check_prices.py, accanto a prices.py:
from prices import discounted
assert discounted(200, 10) == 180.0
assert discounted(80, 25) == 60.0
assert discounted(99, 0) == 99
print("all good")Con la funzione originale:
$ python check_prices.py
all goodCon quella «ripulita»:
$ python check_prices.py
Traceback (most recent call last):
File "/home/you/shop/check_prices.py", line 3, in <module>
assert discounted(200, 10) == 180.0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionErrorLa regressione è stata individuata, il che è un passo avanti. Guarda però cosa ti viene detto: AssertionError, e nient'altro. Non cosa ha effettivamente restituito discounted(200, 10). Inoltre lo script si è fermato al primo fallimento, quindi non sai se gli altri due controlli passano.
Questo capitolo installa lo strumento che risolve entrambi i problemi: pytest.
Alla fine di questo capitolo sarai in grado di
- Spiegare perché i test automatizzati individuano le regressioni che i controlli manuali non vedono
- Installare pytest con
uvopip, e verificarlo conpytest --version - Scrivere un file di test che pytest trova da solo, ed eseguirlo
- Leggere i punti e la riga di riepilogo, e passare da
-qa-v - Spiegare cosa significano i codici di uscita
0e1, e perché è importante
Prerequisiti: prima di scrivere un test. Ti servono anche Python 3 e sapere come scrivere una funzione.
Prima di scrivere il test
Il capitolo precedente riguardava la decisione su cosa testare. Questo riguarda le cose concrete che devono essere a posto prima che la prima riga di codice di test possa essere eseguita. Sono quattro, e ciascuna di esse, se manca, genera un errore diverso e disorientante — quindi vale la pena controllarle in ordine.
1. Una sola cartella di progetto, un solo ambiente. Tutto ciò che si trova in questo capitolo risiede in un'unica cartella:
shop/
├── .venv/ the project's own Python environment
├── pyproject.toml created by uv; lists pytest as a dev dependency
├── prices.py the code under test
└── test_prices.py the testsPerché un ambiente dedicato? Perché pytest deve essere installato nello stesso Python che importerà il tuo codice. Se pytest risiede in un'installazione di Python e i pacchetti del tuo progetto in un'altra, ottieni errori che sembrano indicare codice mancante quando il vero problema è uno strumento mancante. Un .venv per ogni progetto elimina ogni dubbio.
2. pytest installato in quell'ambiente — ce ne occupiamo nella sezione successiva.
3. Il codice importabile. Un file di test è normale codice Python: la sua prima riga sarà from prices import discounted. Quindi, prima di scrivere qualsiasi test, verifica che questo import esatto funzioni dalla cartella del progetto:
$ python -c "from prices import discounted; print(discounted(200, 10))"
180.0Se questa riga fallisce, pytest fallirà allo stesso modo — solo all'interno di un report più lungo. Controllarla da sola richiede cinque secondi e separa "il mio codice non si trova" da "il mio codice è sbagliato". Significa anche che il nome del modulo deve essere un nome Python valido: prices.py può essere importato, my-prices.py e 2prices.py no.
4. La promessa, messa per iscritto sotto forma di casi. Prima di digitare un test, scrivi cosa promette discounted, una riga per ciascun caso:
| Caso | Input | Atteso | | --- | --- | --- | | Sconto tipico | discounted(200, 10) | 180.0 | | Un secondo sconto, diverso | discounted(80, 25) | 60.0 | | Caso limite: nessuno sconto | discounted(99, 0) | 99 |
E altrettanto deliberatamente, cosa non c'è nell'elenco. Una percentuale negativa, o una superiore a 100: la funzione non promette ancora nulla al riguardo, e un test finirebbe solo per congelare qualunque cosa faccia oggi per caso. (Rifiutare un input non valido con un errore è una promessa a sé stante — capitolo quattro). Anche l'aritmetica di Python: stai testando la tua formula, non che * moltiplichi.
Perché due casi ordinari e non uno solo? Guarda la terza riga confrontata con la funzione «ripulita» dall'inizio del capitolo: 99 * (1 - 0 // 100) fa comunque 99. Il caso con zero percento passa anche con il bug. Un piano fatto solo di casi limite può non vedere proprio l'errore che conta, e un piano fatto di un solo caso tipico manca i limiti. Ciascuna riga è lì per individuare qualcosa che le altre non vedrebbero.
Il resto del capitolo trasforma questa tabella in un file di test.
Installare pytest
pytest non fa parte di Python; è un pacchetto che installi nel tuo progetto. È uno strumento per lo sviluppo del codice, non qualcosa di cui il programma ha bisogno durante l'esecuzione — quindi viene inserito come dipendenza di sviluppo.
Se il tuo progetto usa uv, esegui questo comando nella cartella del progetto:
$ uv add --dev pytest
Resolved 7 packages in 59ms
Installed 5 packages in 18ms
+ iniconfig==2.3.1
+ packaging==26.3
+ pluggy==1.6.0
+ pygments==2.21.0
+ pytest==9.1.1uv lo registra in pyproject.toml all'interno di un gruppo dev, così chiunque configuri il progetto in seguito otterrà lo stesso strumento:
[dependency-groups]
dev = [
"pytest>=9.1.1",
]Se usi pip standard all'interno di un ambiente virtuale, attiva l'ambiente ed esegui:
$ python -m pip install pytestpip stampa l'avanzamento del download e termina con:
Successfully installed iniconfig-2.3.1 packaging-26.3 pluggy-1.6.0 pygments-2.21.0 pytest-9.1.1Usare python -m pip anziché il semplice pip assicura che il pacchetto finisca nello stesso Python che eseguirai — una causa comune di "l'ho installato ma non c'è".
Ora controlla che abbia funzionato:
$ pytest --version
pytest 9.1.1Una nota prima di proseguire. Con uv, i comandi vengono eseguiti all'interno dell'ambiente del progetto se preceduti da uv run — quindi uv run pytest --version. Con un ambiente virtuale attivato, è sufficiente pytest da solo. Nel resto di questo corso scriveremo semplicemente pytest; aggiungi uv run davanti se è così che lavori.
Il tuo primo file di test
Due regole sui nomi, e pytest non ha bisogno di nient'altro da parte tua:
- il nome del file inizia con
test_(oppure termina con_test.py) - ogni funzione che vuoi eseguire inizia con
test
Ripristina il file prices.py originale e corretto, e accanto ad esso crea test_prices.py:
from prices import discounted
def test_ten_percent_off():
assert discounted(200, 10) == 180.0
def test_quarter_off():
assert discounted(80, 25) == 60.0
def test_no_discount():
assert discounted(99, 0) == 99Ogni controllo di check_prices.py è ora una piccola funzione a sé stante, e ciascun nome dice cosa viene verificato. Non c'è alcun print, nessuna lista di test da registrare, nessun if __name__ == "__main__". Ora esegui pytest dalla cartella del progetto:
$ pytest
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
rootdir: /home/you/shop
collected 3 items
test_prices.py ... [100%]
============================== 3 passed in 0.01s ===============================pytest ha esaminato la cartella, ha trovato test_prices.py grazie al suo nome, ha trovato le tre funzioni grazie ai loro nomi, ha eseguito ciascuna di esse e ha riportato i risultati.
Leggere l'output
Prendiamolo una riga alla volta.
platform ...— quale Python e quale pytest sono stati eseguiti. Vale la pena darci un'occhiata quando i risultati differiscono tra due macchine.rootdir: /home/you/shop— la cartella che pytest considera come radice del progetto.collected 3 items— quanti test ha trovato. La ricerca dei test si chiama collection. Se questo numero è inferiore alle attese, c'è un nome sbagliato da qualche parte.test_prices.py ...— un carattere per ciascun test, nell'ordine in cui sono stati eseguiti. Un.significa che è passato.[100%]— l'avanzamento lungo l'intera esecuzione.3 passed in 0.01s— la riga di riepilogo. Questa è la riga da leggere per prima.
Ora rimetti la funzione discounted «ripulita» ed esegui di nuovo. Questa volta con -q, che vedremo nel dettaglio tra un momento:
$ pytest -q
FF. [100%]
=================================== FAILURES ===================================
_____________________________ test_ten_percent_off _____________________________
def test_ten_percent_off():
> assert discounted(200, 10) == 180.0
E assert 200 == 180.0
E + where 200 = discounted(200, 10)
test_prices.py:5: AssertionError
_______________________________ test_quarter_off _______________________________
def test_quarter_off():
> assert discounted(80, 25) == 60.0
E assert 80 == 60.0
E + where 80 = discounted(80, 25)
test_prices.py:9: AssertionError
=========================== short test summary info ============================
FAILED test_prices.py::test_ten_percent_off - assert 200 == 180.0
FAILED test_prices.py::test_quarter_off - assert 80 == 60.0
2 failed, 1 passed in 0.01sLa riga di avanzamento ora è FF.: una F per ciascun fallimento. E confronta questo con il semplice AssertionError di prima:
- ogni test è stato eseguito — un fallimento in uno non ha fermato gli altri, quindi puoi vedere che lo zero percento funziona ancora
- ogni fallimento indica il nome del test e la riga
assert 200 == 180.0mostra il valore che la funzione ha effettivamente restituito
Leggere questo report per intero è l'argomento di tutto il prossimo capitolo. Per ora, nota solo che la riga di riepilogo è cambiata in 2 failed, 1 passed — e che lo stesso assert che già conoscevi ora si spiega da solo.
-q e -v: meno e più
L'output predefinito è una via di mezzo. Due flag lo spostano in una direzione o nell'altra.
-q (quiet) elimina l'intestazione:
$ pytest -q
... [100%]
3 passed in 0.01s-v (verbose) assegna a ogni test una riga dedicata, con il suo nome completo:
$ pytest -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
cachedir: .pytest_cache
rootdir: /home/you/shop
collecting ... collected 3 items
test_prices.py::test_ten_percent_off PASSED [ 33%]
test_prices.py::test_quarter_off PASSED [ 66%]
test_prices.py::test_no_discount PASSED [100%]
============================== 3 passed in 0.01s ===============================test_prices.py::test_ten_percent_off è il node ID del test: il file, un doppio due punti, la funzione. Lo userai più avanti per eseguire un singolo test. È anche qui che i nomi descrittivi dei test si rivelano utili — in un elenco con -v, test_quarter_off ti dice cosa è passato; test_2 non lo farebbe.
Usa -q quando esegui i test continuamente e vuoi solo il verdetto, e -v quando vuoi vedere esattamente quali test sono stati eseguiti.
Cos'è .pytest_cache? Una cartella creata da pytest per ricordare informazioni tra un'esecuzione e l'altra, ad esempio quali test sono falliti l'ultima volta. È sicuro eliminarla e non deve essere inclusa nel controllo versione.Codici di uscita: come una macchina legge il risultato
Tu leggi la riga di riepilogo. Uno script, un editor o un server di build non possono farlo — leggono il codice di uscita, il numero che ogni programma restituisce alla shell quando termina. In una shell Unix, $? contiene l'ultimo restituito:
$ pytest -q
... [100%]
3 passed in 0.01s
$ echo $?
0E con la funzione rotta, dopo l'esecuzione terminata con 2 failed, 1 passed:
$ echo $?
10— tutto raccolto, tutto passato1— i test sono stati eseguiti, e almeno uno è fallito
(In PowerShell la variabile è $LASTEXITCODE.) Questo è ciò che permette ai test di essere eseguiti automaticamente prima di unire il codice: un codice di uscita diverso da zero interrompe il processo. Ci sono altri codici, uno dei quali incontrerai in «Quando qualcosa va storto» qui sotto.
Dove si trovano i file di test
In questo capitolo il file di test si trova proprio accanto al codice:
shop/
├── prices.py
└── test_prices.pyQuesto va benissimo per una manciata di file. La maggior parte dei progetti raccoglie invece i test in una cartella tests/ dedicata. In entrambi i casi pytest li trova in base al nome; come organizzare la struttura di un progetto reale, e cosa cambia quando lo fai, è l'argomento del capitolo tre.
Un esempio completo
Una funzione che converte un punteggio in un voto in lettere, grades.py:
def letter_grade(score):
"""Turn a score from 0 to 100 into a letter."""
if score >= 80:
return "A"
if score >= 60:
return "B"
if score >= 40:
return "C"
return "F"E i suoi test, test_grades.py:
from grades import letter_grade
def test_top_score_is_an_a():
assert letter_grade(95) == "A"
def test_exactly_eighty_is_still_an_a():
assert letter_grade(80) == "A"
def test_just_below_eighty_is_a_b():
assert letter_grade(79) == "B"
def test_middle_score_is_a_c():
assert letter_grade(50) == "C"
def test_zero_is_an_f():
assert letter_grade(0) == "F"$ pytest -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
cachedir: .pytest_cache
rootdir: /home/you/grades
collecting ... collected 5 items
test_grades.py::test_top_score_is_an_a PASSED [ 20%]
test_grades.py::test_exactly_eighty_is_still_an_a PASSED [ 40%]
test_grades.py::test_just_below_eighty_is_a_b PASSED [ 60%]
test_grades.py::test_middle_score_is_a_c PASSED [ 80%]
test_grades.py::test_zero_is_an_f PASSED [100%]
============================== 5 passed in 0.01s ===============================Ci sono due cose che vale la pena notare.
Innanzitutto, i test su 80 e 79. Si trovano ai due lati di un valore limite, che è dove risiedono gli errori. Modifica score >= 80 in score > 80 — una svista facile — ed esegui la versione sintetica:
$ pytest -q
.F... [100%]
=================================== FAILURES ===================================
______________________ test_exactly_eighty_is_still_an_a _______________________
def test_exactly_eighty_is_still_an_a():
> assert letter_grade(80) == "A"
E AssertionError: assert 'B' == 'A'
E
E - A
E + B
test_grades.py:9: AssertionError
=========================== short test summary info ============================
FAILED test_grades.py::test_exactly_eighty_is_still_an_a - AssertionError: as...
1 failed, 4 passed in 0.01sIl secondo test, test_exactly_eighty_is_still_an_a, è quello che fallisce: 80 ora riceve 'B'. Un test soltanto a 95 non se ne sarebbe mai accorto.
In secondo luogo, i nomi. Nell'elenco di -v ogni riga si legge come una frase sul comportamento della funzione. Quando uno di essi fallirà tra qualche mese, il nome da solo ti dirà quale promessa è stata infranta.
Quando qualcosa va storto
zsh: command not found: pytest (oppure bash: pytest: command not found) La shell non riesce a trovare il programma pytest. O non è installato, o è installato in un ambiente virtuale non attivo. Con uv, esegui uv run pytest; con pip, attiva prima l'ambiente. Funziona anche python -m pytest, che esegue pytest con qualsiasi Python a cui fa riferimento python.
/usr/bin/python3: No module named pytest Hai eseguito python -m pytest, e quel Python non contiene pytest. Il percorso all'inizio ti indica quale Python è stato usato — qui quello di sistema, non quello del tuo progetto. Installa pytest nell'ambiente che intendi usare.
ModuleNotFoundError: No module named 'price' durante la raccolta Il file di test non è riuscito a importare il tuo codice — qui a causa di un refuso, from price import ... invece di from prices import ...:
$ pytest -q
==================================== ERRORS ====================================
_______________________ ERROR collecting test_prices.py ________________________
ImportError while importing test module '/home/you/shop/test_prices.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
test_prices.py:1: in <module>
from price import discounted
E ModuleNotFoundError: No module named 'price'
=========================== short test summary info ============================
ERROR test_prices.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 error in 0.01sNota la parola ERROR, non FAILED: nessun test è stato eseguito, perché il file non è stato nemmeno caricato, e il codice di uscita è 2. Leggi l'ultima riga con E, poi esegui il controllo dell'import visto in «Prima di scrivere il test» — python -c "from prices import discounted" — finché non funziona da solo.
no tests ran in 0.01s pytest è stato eseguito, ma non ha raccolto nulla. Il nome del file non inizia con test_ (tests_prices.py, prices_tests.py e test.py vengono tutti ignorati), oppure le funzioni non iniziano con test (check_quarter_off viene ignorata silenziosamente). Il codice di uscita qui è 5, non 0, così un'esecuzione automatica non scambia "nessun test trovato" per "tutti superati".
Un test passa quando chiaramente non dovrebbe Cerca un assert mancante:
from prices import discounted
def test_quarter_off():
discounted(80, 25) == 999$ pytest -q
. [100%]
1 passed in 0.01sIl confronto viene calcolato e scartato. Un test fallisce solo quando qualcosa al suo interno solleva un'eccezione; senza assert, non viene sollevato nulla.
PytestReturnNotNoneWarning: Test functions should return None Hai scritto return discounted(99, 0) == 99 invece di assert .... pytest ignora ciò che un test restituisce, quindi questo test passa indipendentemente dal fatto che il confronto sia vero o falso. L'avviso chiede persino: Did you mean to use assert instead of return?
Passo 4 di 6 — Prevedi
Verifica cosa hai capito
Questo file viene salvato come test_double.py e viene eseguito pytest -q. Quali caratteri compaiono all'inizio della prima riga dell'output?
def double(n):
return n * 2
def test_two():
assert double(2) == 4
def test_zero():
assert double(0) == 1
def test_negative():
assert double(-3) == -6- AF
- B..F
- C.F.
- DF..
999 è palesemente la risposta sbagliata, eppure pytest riporta 1 passed. Perché?
def discounted(price, percent):
return price - price * percent / 100
def test_quarter_off():
discounted(80, 25) == 999- Apytest controlla solo che la funzione sia stata eseguita, non il suo risultato
- BLa riga non contiene `assert` — il confronto viene calcolato e scartato
- CI numeri decimali non possono essere confrontati con `==`
- DIl nome della funzione non inizia con `test_`
Un file contenente test corretti si chiama double_checks.py. Cosa succede quando esegui pytest in quella cartella?
- Apytest si interrompe con un `ImportError`
- B`no tests ran` — il nome del file non inizia con `test_`, quindi non viene mai esaminato; codice di uscita `5`
- CViene eseguito ogni test, perché i nomi delle funzioni iniziano con `test_`
- Dpytest rinomina il file in `test_double_checks.py` al posto tuo
Per rispondere serve un account
Accedi per verificare le tue risposte
Le domande sono qui sopra, e ragionarci a mente è la parte che conta. Accedi per vedere le risposte, le spiegazioni e i suggerimenti a tre livelli.
Tocca a te
Crea una cartella con un file temperature.py contenente due funzioni:
c_to_f(celsius)— restituiscecelsius * 9 / 5 + 32f_to_c(fahrenheit)— restituisce(fahrenheit - 32) * 5 / 9
Poi installa pytest nell'ambiente di quella cartella e verificalo con pytest --version.
Scrivi test_temperature.py con almeno cinque test. Includi il punto di congelamento dell'acqua (0 e 32), il punto di ebollizione dell'acqua (100 e 212), e -40, che è lo stesso in entrambe le scale. Assegna a ciascun test un nome che indichi cosa viene verificato.
Eseguilo in tre modi — pytest, pytest -q e pytest -v — e dopo uno di essi, controlla il codice di uscita.
Poi rompilo di proposito: cambia 9 / 5 in 5 / 9 in c_to_f. Prima di eseguire, prevedi quali test falliranno e come apparirà la riga di avanzamento. Esegui pytest -q, confronta il risultato e controlla di nuovo il codice di uscita.
Infine, rinomina test_temperature.py in temperature_check.py ed esegui pytest. Leggi cosa dice e il suo codice di uscita. Poi ripristina il nome originale. Sapere che aspetto ha "nessun test trovato" ti risparmierà un pomeriggio di confusione.
Soluzione
Prima di tutto il piano, prima di scrivere qualsiasi codice — la stessa tabella di prima:
| Caso | Input | Atteso | | --- | --- | --- | | L'acqua gela | c_to_f(0), f_to_c(32) | 32, 0 | | L'acqua bolle | c_to_f(100), f_to_c(212) | 212, 100 | | Le due scale si incontrano | c_to_f(-40), f_to_c(-40) | -40, -40 |
temperature.py:
def c_to_f(celsius):
return celsius * 9 / 5 + 32
def f_to_c(fahrenheit):
return (fahrenheit - 32) * 5 / 9Prima di scrivere i test, il controllo dell'import dalla cartella del progetto:
$ python -c "import temperature; print('importable')"
importabletest_temperature.py:
from temperature import c_to_f, f_to_c
def test_water_freezes_at_32_f():
assert c_to_f(0) == 32
def test_water_boils_at_212_f():
assert c_to_f(100) == 212
def test_32_f_is_zero_c():
assert f_to_c(32) == 0
def test_212_f_is_100_c():
assert f_to_c(212) == 100
def test_minus_40_is_the_same_in_both_scales():
assert c_to_f(-40) == -40
assert f_to_c(-40) == -40$ pytest -q
..... [100%]
5 passed in 0.01s
$ echo $?
0
$ pytest -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
cachedir: .pytest_cache
rootdir: /home/you/temperature
collecting ... collected 5 items
test_temperature.py::test_water_freezes_at_32_f PASSED [ 20%]
test_temperature.py::test_water_boils_at_212_f PASSED [ 40%]
test_temperature.py::test_32_f_is_zero_c PASSED [ 60%]
test_temperature.py::test_212_f_is_100_c PASSED [ 80%]
test_temperature.py::test_minus_40_is_the_same_in_both_scales PASSED [100%]
============================== 5 passed in 0.01s ===============================Ora il bug inserito di proposito, 9 / 5 modificato in 5 / 9 all'interno di c_to_f:
$ pytest -q
.F..F [100%]
=================================== FAILURES ===================================
__________________________ test_water_boils_at_212_f ___________________________
def test_water_boils_at_212_f():
> assert c_to_f(100) == 212
E assert 87.55555555555556 == 212
E + where 87.55555555555556 = c_to_f(100)
test_temperature.py:9: AssertionError
___________________ test_minus_40_is_the_same_in_both_scales ___________________
def test_minus_40_is_the_same_in_both_scales():
> assert c_to_f(-40) == -40
E assert 9.777777777777779 == -40
E + where 9.777777777777779 = c_to_f(-40)
test_temperature.py:21: AssertionError
=========================== short test summary info ============================
FAILED test_temperature.py::test_water_boils_at_212_f - assert 87.55555555555...
FAILED test_temperature.py::test_minus_40_is_the_same_in_both_scales - assert...
2 failed, 3 passed in 0.01s
$ echo $?
1E il file rinominato:
$ pytest -q
no tests ran in 0.01s
$ echo $?
5Perché è scritto in questo modo:
- Un comportamento per ciascun test, con un nome formulato come una frase. Nell'elenco di
-v, ogni riga dichiara cosa viene promesso. Quandotest_water_boils_at_212_ffallisce, sai cosa si è rotto prima ancora di continuare a leggere. - Valori noti e verificabili.
0/32,100/212e-40sono fatti che puoi verificare senza calcolatrice, quindi un fallimento punta al codice, non a un valore atteso dubbio. - Entrambe le direzioni.
c_to_fef_to_csono codice separato, quindi ciascuna richiede i propri test; il fatto che una sia corretta non dice nulla sull'altra. - L'esecuzione con il bug è la parte più significativa. Solo due test sono falliti, e
test_water_freezes_at_32_fnon era tra questi:0 * 5 / 9 + 32fa comunque32, perché moltiplicare per zero nasconde qualsiasi moltiplicatore. Questo è esattamente il motivo per cui il piano contiene più di un caso — una suite con solo il punto di congelamento sarebbe passata anche in presenza del bug. Il test per-40ha due righe conassert; la prima è fallita, quindi la seconda non è mai stata eseguita. Qui è accettabile perché entrambe descrivono la stessa promessa. - I codici di uscita —
0,1,5— sono i tre che incontrerai più spesso: tutti passati, qualcosa è fallito, nessun test trovato.
Passo 6 di 6
Sfida — il quiz del capitolo
Dieci domande dalla più facile alla più difficile. Le ultime sono difficili di proposito.
Accedi per fare il quiz