Amb la v0.20 el projecte ja té historial: pots tornar a qualsevol punt anterior i saps qui va canviar què i per què. Però Git no respon a la pregunta que de debò importa cada vegada que toques una línia: continua funcionant tota la resta? Fins ara la resposta la donaves a mà, engegant python -m tascafacil, creant una tasca, marcant-la, filtrant, desant i sortint, opció per opció. Això costa cinc minuts, es fa malament a la tercera vegada i s'acaba ometent justament el dia que calia. Aquesta lliçó construeix la xarxa de seguretat que falta: proves automatitzades que comproven soles, en menys d'un segon, que el programa fa el que ha de fer. Veuràs el patró que segueixen totes les proves, les dues eines de l'ecosistema Python —unittest, que ve inclosa, i pytest, la que fa servir tothom—, què val la pena provar i què no, i per què la separació entre entrada/sortida i lògica que vam fer a 04-04 era, en realitat, la preparació per a aquest moment.

Contingut

  1. Per què provar
  2. Tipus de prova
  3. Què fa provable un codi
  4. El patró AAA i el nom del test
  5. assert: la primera prova
  6. unittest, la biblioteca estàndard
  7. pytest, l'eina de l'ecosistema
  8. unittest davant de pytest
  9. Què provar i què no
  10. Cobertura
  11. TDD: vermell, verd, refactoritzar
  12. TascaFàcil v0.21: la carpeta tests/
  13. Errors habituals i consells
  14. Exercicis
  15. Conclusió

  1. Per què provar

Una prova automatitzada és simplement codi que executa el teu codi i comprova que el resultat és l'esperat. Sona a feina extra, i ho és la primera vegada; deixa de ser-ho així que sumes el que costa el contrari:

  • Repassar el menú a mà costa minuts a cada canvi, i es fa pitjor amb el temps: al principi comproves els deu casos, a la desena vegada només el que has tocat, i l'error apareix justament en un altre.
  • La por paralitza: sense proves, millorar codi que ja funciona sembla una temeritat, així que ningú no el millora i el projecte es podreix.
  • Una prova és documentació que no menteix: test_completar_iguala_fets_a_dies explica el comportament esperat millor que qualsevol paràgraf, i si el comportament canvia, la prova falla.

La paraula clau és regressió: un error que apareix en alguna cosa que abans funcionava. Les proves automatitzades són l'única defensa pràctica contra elles, i són el que permet refactoritzar sense por, que és exactament el que farem a Estil i refactorització.

  1. Tipus de prova

Tipus Què comprova Mida i velocitat Exemple a TascaFàcil
Unitària Una peça aïllada: una funció o un mètode Molt ràpida, mil·lisegons Tasca.progres() retorna 50.0 amb 2 de 4 dies
D'integració Que diverses peces encaixen entre si Més lenta Desar l'agenda en JSON i tornar-la a carregar
De sistema El programa sencer, tal com el fa servir l'usuari Lenta i fràgil Recórrer el menú simulant pulsacions

Aquest curs se centra en les unitàries, amb alguna d'integració, i per una raó pràctica: són ràpides, assenyalen amb precisió què està trencat i no depenen de la interfície. Una prova de sistema que recorre el menú es trenca així que canvies un text, i quan falla no diu on és el problema. La proporció sana en qualsevol projecte és la mateixa: moltes unitàries, algunes d'integració, molt poques de sistema.

  1. Què fa provable un codi

Aquí es cobra el deute de Descompondre un programa en funcions, on vam separar l'entrada i la sortida de la lògica i vam dir que això permetria provar el codi. Compara aquestes dues versions:

# NO ES POT PROVAR BE: demana per teclat i imprimeix
def calcular_progres():
    fets = int(input("Dies fets: "))
    dies = int(input("Dies totals: "))
    print(f"Progres: {fets / dies * 100:.1f}%")

# SI QUE ES POT PROVAR: rep dades i retorna un valor
def progres(fets: int, dies: int) -> float:
    """Retorna el percentatge d avanc, entre 0.0 i 100.0."""
    return min(100.0, fets / dies * 100)

La primera és impossible de provar de manera raonable: per executar-la cal simular el teclat, i per comprovar el resultat cal capturar el que imprimeix. La segona es prova en una línia: progres(2, 4) == 50.0. La regla que se'n dedueix és la que ja coneixes amb un altre nom: les funcions que reben dades i retornen resultats són provables; les que parlen amb el món, no. Per això model.py i agenda.py no tenen ni un sol print, i per això tot el que es pugui comprovar viu allà i no a interficie.py.

  1. El patró AAA i el nom del test

Totes les proves del món tenen la mateixa estructura de tres passos, coneguda com a AAA:

  1. Arrange (preparar): crear les dades i els objectes que necessita la prova.
  2. Act (actuar): executar exactament una cosa, la que s'està provant.
  3. Assert (comprovar): verificar que el resultat és l'esperat.
def test_completar_marca_la_tasca_i_iguala_els_dies():
    tasca = Tasca("Cartell fira del llibre", "luis", "alta", 4)  # Arrange
    tasca.completar()                                            # Act
    assert tasca.completada is True                              # Assert
    assert tasca.fets == 4

I el nom importa tant com el contingut, perquè és l'única cosa que veuràs quan la prova falli. Un bon nom diu què es prova i què s'espera: test_completar_marca_la_tasca_i_iguala_els_dies t'estalvia obrir el fitxer; test_1 o test_tasca no diuen res. La convenció habitual és test_<que>_<condicio>_<resultat esperat>, i no importa que quedi llarg: els noms de les proves es llegeixen, no s'escriuen a mà.

  1. assert: la primera prova

Abans de fer servir cap eina, assert ja et permet comprovar coses. La seva forma és assert condicio, "missatge si falla": si la condició és certa no passa res, i si és falsa llança un AssertionError amb el teu missatge.

from tascafacil.model import Tasca

tasca = Tasca("Logotip Sole", "nuria", "alta", 4)
assert tasca.prioritat == "alta", "la prioritat s hauria de normalitzar"
assert tasca.progres() == 0.0, "una tasca nova no te avanc"
tasca.fets = 2
assert tasca.progres() == 50.0, f"esperava 50.0 i ha sortit {tasca.progres()}"
print("Totes les comprovacions han passat.")

Desat en un fitxer i executat, o passa en silenci o peta assenyalant la línia. És un primer pas perfectament vàlid, i també la raó que a 08-02 diguéssim que el lloc natural de l'assert són les proves. Els seus límits apareixen de seguida: s'atura al primer error sense comprovar la resta, no agrupa res, no dona cap informe i no hi ha manera d'executar-ne només una part. Per a això hi ha les eines.

  1. unittest, la biblioteca estàndard

unittest ve amb Python, així que no hi ha res a instal·lar. Les seves proves són mètodes que comencen per test_ dins d'una classe que hereta d'unittest.TestCase:

# tests/test_model.py
import unittest
from tascafacil.model import Tasca, TascaInvalida

class TestTasca(unittest.TestCase):
    def setUp(self):
        """S executa ABANS de cada test: cadascun parteix del mateix."""
        self.tasca = Tasca("Cartell fira del llibre", "luis", "alta", 4)

    def test_normalitza_el_responsable_a_minuscules(self):
        tasca = Tasca("Pressupost Vidal", "  MARTA ", "mitjana", 2)
        self.assertEqual(tasca.responsable, "marta")

    def test_completar_iguala_els_dies_fets(self):
        self.tasca.completar()
        self.assertTrue(self.tasca.completada)
        self.assertEqual(self.tasca.fets, 4)

    def test_prioritat_invalida_llanca_excepcio(self):
        with self.assertRaises(TascaInvalida):
            Tasca("Cartell", "luis", "urgent", 3)

Quatre peces que convé entendre. setUp s'executa abans de cada mètode de prova, de manera que cap prova no hereta l'estat d'una altra —hi ha també tearDown, que s'executa després i serveix per netejar—. Els mètodes assertEqual, assertTrue, assertIn comproven igualtat, veritat i pertinença. I assertRaises, fet servir amb with, comprova el contrari de l'habitual: que el bloc que llanci aquesta excepció; si no la llança, la prova falla. S'executa des de la carpeta del projecte:

$ python -m unittest discover -s tests -v
test_completar_iguala_els_dies_fets (test_model.TestTasca) ... ok
test_normalitza_el_responsable_a_minuscules (test_model.TestTasca) ... ok
test_prioritat_invalida_llanca_excepcio (test_model.TestTasca) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s   OK

  1. pytest, l'eina de l'ecosistema

pytest no ve amb Python, però és el que fa servir pràcticament tothom, perquè treu la cerimònia: no calen classes i es comprova amb l'assert normal.

pip install pytest
# tests/test_agenda.py
import pytest
from tascafacil.model import Tasca, TascaInvalida
from tascafacil.agenda import Agenda

def test_ordenades_posa_primer_la_prioritat_alta():
    agenda = Agenda()
    agenda.afegir(Tasca("Pressupost Vidal", "marta", "baixa", 2))
    agenda.afegir(Tasca("Cartell fira", "luis", "alta", 4))
    assert [t.prioritat for t in agenda.ordenades()] == ["alta", "baixa"]

def test_prioritat_invalida_llanca_tasca_invalida():
    with pytest.raises(TascaInvalida):
        Tasca("Cartell", "luis", "urgent", 3)

@pytest.mark.parametrize("fets, dies, esperat", [
    (0, 4, 0.0), (2, 4, 50.0), (4, 4, 100.0), (8, 4, 100.0),
])
def test_progres_calcula_el_percentatge(fets, dies, esperat):
    tasca = Tasca("Cartell", "luis", "alta", dies)
    tasca.fets = fets
    assert tasca.progres() == esperat

Tres eines noves aquí. pytest.raises fa el mateix que assertRaises. @pytest.mark.parametrize és la més rendible de totes: escriu una prova i executa-la amb quatre jocs de dades, que apareixen com quatre proves independents a l'informe; sense ella hauries escrit quatre funcions gairebé idèntiques. I fixa't en l'últim cas, (8, 4, 100.0): és un cas límit, més dies fets que previstos, exactament el tipus de cosa que la parametrització convida a afegir.

Les fixtures són la manera que té pytest de preparar dades comunes, l'equivalent del setUp. Es declaren amb un decorador i es demanen pel nom com a paràmetre:

@pytest.fixture
def agenda_amb_tres():
    """Agenda d exemple de l Estudi Alba."""
    agenda = Agenda()
    for t in (Tasca("Cartell fira", "luis", "alta", 4),
              Tasca("Logotip Sole", "nuria", "mitjana", 5),
              Tasca("Pressupost Vidal", "marta", "baixa", 2)):
        agenda.afegir(t)
    return agenda

def test_filtrar_per_responsable_retorna_nomes_les_seves(agenda_amb_tres):
    resultat = agenda_amb_tres.filtrar_per("responsable", "luis")
    assert len(resultat) == 1
    assert resultat[0].titol == "Cartell fira"

I hi ha una fixture incorporada que resol un problema molt real: tmp_path, una carpeta temporal diferent per a cada prova, que Python esborra en acabar. És el que permet provar el desat en fitxer sense tocar el tasques.json de debò:

def test_desar_i_carregar_conserva_les_tasques(agenda_amb_tres, tmp_path):
    ruta = tmp_path / "tasques.json"         # carpeta temporal, no la real
    magatzem.desar(agenda_amb_tres, ruta)
    recuperada = magatzem.carregar_tasques(ruta)
    assert len(recuperada) == 3
    assert recuperada.cercar("Logotip Sole").responsable == "nuria"

Això és una prova d'integració: comprova que Agenda, to_dict, from_dict i el mòdul magatzem encaixen entre si. I comprova l'única cosa que importa de la persistència: que el que hi entra en torna a sortir igual (un cicle desar → carregar, o round-trip).

  1. unittest davant de pytest

Aspecte unittest pytest
Instal·lació Inclòs a Python pip install pytest
Estructura Classes que hereten de TestCase Funcions soltes
Comprovació self.assertEqual(a, b) assert a == b
Excepcions with self.assertRaises(E): with pytest.raises(E):
Dades comunes setUp / tearDown Fixtures
Diversos casos Un mètode per cas @parametrize
Informe d'error Escarransit Molt detallat: mostra els valors reals
Execució python -m unittest discover pytest

Totes dues són correctes i conviuen: pytest executa també les proves escrites amb unittest. La recomanació pràctica: aprèn unittest perquè el trobaràs en projectes antics i perquè no requereix instal·lar res, i escriu amb pytest perquè és més curt, més llegible i el seu informe d'errors et diu quin valor ha sortit i quin esperaves.

  1. Què provar i què no

No es prova tot: es prova el que es pot trencar i fa mal. Per a cada funció val la pena cobrir tres famílies de casos:

  • Casos normals: el que passa el 90 % de les vegades. Una tasca amb 2 de 4 dies dona 50 %.
  • Casos límit: les vores, on viu la majoria dels errors. Zero dies fets, agenda buida, la llista amb un sol element, més dies fets que previstos, textos amb espais o en majúscules.
  • Casos d'error: el que ha de fallar i com ha de fallar. Una prioritat inventada ha de llançar TascaInvalida, no retornar None en silenci.

I hi ha un parany clàssic: provar la implementació en lloc del comportament. Una prova que comprova que Agenda desa internament una llista anomenada _tasques es trencarà el dia que canviïs aquesta llista per un diccionari, encara que el programa continuï funcionant perfectament. Prova sempre el que promet la funció —el que retorna, el que llança, com queda l'objecte—, mai les seves entranyes. El senyal d'alarma és fàcil de reconèixer: si en refactoritzar sense canviar el comportament es trenquen les proves, les proves estaven mal escrites.

  1. Cobertura

La cobertura mesura quin percentatge de les teves línies executen les proves. S'obté amb una eina externa:

pip install coverage
coverage run -m pytest
coverage report -m

L'informe llista cada fitxer amb el seu percentatge i les línies que no s'han executat mai, i aquí hi ha la seva veritable utilitat: descobrir branques senceres que ningú no prova —típicament els except—. Però convé entendre'n el límit: la cobertura mesura què s'executa, no què es comprova. Un test que cridi totes les funcions sense ni un assert dona el 100 % i no garanteix absolutament res. Un 90 % amb proves que comproven els casos límit val infinitament més que un 100 % buit. Fes-la servir per trobar buits, mai com a objectiu.

  1. TDD: vermell, verd, refactoritzar

TDD (Test-Driven Development, desenvolupament dirigit per proves) capgira l'ordre habitual: primer s'escriu la prova, i només després el codi que la fa passar. El cicle té tres passos:

flowchart LR
    A[VERMELL: escriu una prova que falla] --> B[VERD: el codi minim perque passi]
    B --> C[REFACTORITZAR: neteja sense trencar res]
    C --> A

Escriure la prova primer obliga a decidir què ha de fer la funció abans de pensar en com, i garanteix que la prova de debò comprova alguna cosa: si mai no l'has vista fallar, no saps si funciona. No cal aplicar TDD sempre —en aquest curs no ho farem—, però hi ha un cas en què és clarament la millor opció: quan arregles un error. Escriu primer una prova que el reprodueixi (vermell), arregla'l (verd) i aquesta prova quedarà per sempre impedint que l'error torni.

  1. TascaFàcil v0.21: la carpeta tests/

Les proves viuen a la seva pròpia carpeta, al costat del paquet:

tascafacil/          <- el projecte: tascafacil/ (el paquet) i README.md
└── tests/
    ├── test_model.py
    └── test_agenda.py

Els fitxers de tests/ són els que has vist als apartats 6 i 7: test_model.py comprova la validació de Tasca, la normalització del responsable i completar(); test_agenda.py comprova l'ordre d'ordenades(), el filtre per responsable i el cicle desar/carregar amb tmp_path. I aquesta és una execució real, amb un error de debò:

$ pytest -q
....F...
=================================== FAILURES ===================================
_________________ test_progres_calcula_el_percentatge[8-4-100.0] _______________
    def test_progres_calcula_el_percentatge(fets, dies, esperat):
        tasca.fets = fets
>       assert tasca.progres() == esperat
E       assert 200.0 == 100.0
E        +  where 200.0 = <Tasca 'Cartell'>.progres()
tests/test_model.py:34: AssertionError
1 failed, 7 passed in 0.06s

La prova en vermell és el cas límit (8, 4, 100.0): amb més dies fets que previstos, progres() retornava 200 %. L'informe de pytest ho diu tot: quin cas concret ha fallat, quin valor ha sortit (200.0) i quin s'esperava (100.0). L'arreglada és una línia a model.py:

    def progres(self) -> float:
        """Retorna el percentatge d avanc, entre 0.0 i 100.0."""
        return min(100.0, self.fets / self.dies * 100)       # abans, sense min()
$ pytest -q
........
8 passed in 0.05s

TascaFàcil v0.21: vuit proves que s'executen en cinc centèsimes de segon i verifiquen el que abans exigia recórrer el menú a mà. A partir d'aquí, cada canvi es valida amb una sola comanda, i el projecte acaba de guanyar la xarxa de seguretat que calia per poder-lo millorar sense por. Existeixen a més eines d'integració contínua que executen aquestes mateixes proves automàticament a cada push al remot; queda molt lluny d'aquest curs, però és bo saber que el pas següent existeix.

Errors Habituals i Consells

  • Provar funcions que fan input() i print(). No es pot. Extreu la lògica a una funció que rebi dades i retorni un valor, i prova aquesta.
  • Proves que depenen les unes de les altres. Cada prova ha de poder executar-se sola i en qualsevol ordre. Per a això hi ha setUp i les fixtures.
  • Tocar les dades reals. Una prova que escriu a tasques.json t'esborrarà la feina algun dia. Fes servir tmp_path.
  • Provar la implementació, o posar noms com test_1 i test_tasca. Si en refactoritzar sense canviar el comportament es trenquen les proves, estaven mal escrites; i el nom és l'única cosa que veus quan alguna cosa falla.
  • Perseguir el 100 % de cobertura. És fàcil de falsejar i no garanteix res. Cobreix els casos límit i els d'error, que és on són els errors.
  • Consell: quan trobis un error, escriu primer la prova que el reprodueix. Et confirma que l'has entès i evita que torni mai més.

Exercicis

Exercici 1: Fer provable una funció

Aquesta funció no es pot provar. Separa-la en dues —una amb la lògica i una altra amb la conversa— i escriu dues proves amb pytest per a la part lògica, incloent-hi un cas límit:

def registrar_hores():
    hores = float(input("Hores treballades: "))
    tarifa = float(input("Tarifa per hora: "))
    total = hores * tarifa
    if hores > 8:
        total += (hores - 8) * tarifa * 0.25      # recarrec per hora extra
    print(f"Total: {total:.2f} EUR")

Exercici 2: Casos normals, límit i d'error

Escriu les proves d'Agenda.cercar(titol), que retorna la tasca amb aquest títol (sense distingir majúscules) o None si no existeix. Cobreix un cas normal, dos casos límit i comprova també què passa amb una agenda buida. Fes servir una fixture per a l'agenda d'exemple.

Exercici 3: Parametritzar i provar fitxers

Converteix aquestes tres proves gairebé idèntiques en una de sola parametritzada, i escriu a més una prova del cicle desar/carregar fent servir tmp_path:

def test_prioritat_alta_es_valida():
    assert Tasca("A", "luis", "alta", 1).prioritat == "alta"
def test_prioritat_mitjana_es_valida():
    assert Tasca("A", "luis", "MITJANA", 1).prioritat == "mitjana"
def test_prioritat_baixa_es_valida():
    assert Tasca("A", "luis", " baixa ", 1).prioritat == "baixa"

Solucions

Solució 1.

# logica.py: rep dades, retorna un valor. Provable.
JORNADA = 8
RECARREC_EXTRA = 0.25

def cost_hores(hores: float, tarifa: float) -> float:
    """Retorna el cost total, amb recarrec del 25% a partir de 8 hores."""
    total = hores * tarifa
    if hores > JORNADA:
        total += (hores - JORNADA) * tarifa * RECARREC_EXTRA
    return round(total, 2)

def registrar_hores() -> None:                 # la conversa, a part
    """Demana les dades per teclat i mostra el cost."""
    hores = float(input("Hores treballades: "))
    tarifa = float(input("Tarifa per hora: "))
    print(f"Total: {cost_hores(hores, tarifa):.2f} EUR")

# tests/test_logica.py
def test_vuit_hores_justes_no_porten_recarrec():    # cas limit
    assert cost_hores(8, 30) == 240.0

def test_hores_extra_apliquen_el_recarrec():
    assert cost_hores(10, 30) == 315.0              # 240 + 2*30*1.25

El cas límit de les vuit hores exactes és el més valuós, perquè és on viu l'error clàssic: un >= en lloc d'un > cobraria recàrrec per una jornada normal, i només aquesta prova ho detectaria. Fixa't a més que la funció de conversa ha quedat tan simple que ja no necessita prova: l'única cosa que fa és cridar la que sí que està provada.

Solució 2.

@pytest.fixture
def agenda():
    a = Agenda()
    a.afegir(Tasca("Cartell fira", "luis", "alta", 4))
    a.afegir(Tasca("Logotip Sole", "nuria", "mitjana", 5))
    return a

def test_cercar_troba_una_tasca_existent(agenda):
    assert agenda.cercar("Cartell fira").responsable == "luis"

def test_cercar_no_distingeix_majuscules(agenda):         # cas limit
    assert agenda.cercar("CARTELL FIRA") is not None

def test_cercar_retorna_none_si_no_existeix(agenda):      # cas limit
    assert agenda.cercar("Tasca inventada") is None

def test_cercar_en_agenda_buida_retorna_none():
    assert Agenda().cercar("Cartell fira") is None

Les quatre proves descriuen el contracte complet de cercar: troba, ignora majúscules, retorna None quan no hi ha coincidència i no peta amb una agenda buida. Cap no mira com està desada la col·lecció per dins, així que si demà Agenda canvia la seva llista per un diccionari, aquestes proves continuaran passant: comproven el comportament, no la implementació.

Solució 3.

@pytest.mark.parametrize("entrada, esperada", [
    ("alta", "alta"), ("MITJANA", "mitjana"), (" baixa ", "baixa"),
])
def test_la_prioritat_es_normalitza(entrada, esperada):
    assert Tasca("A", "luis", entrada, 1).prioritat == esperada

def test_desar_i_carregar_conserva_les_dades(tmp_path):
    ruta = tmp_path / "tasques.json"
    agenda = Agenda()
    agenda.afegir(Tasca("Cartell fira", "luis", "alta", 4))
    magatzem.desar(agenda, ruta)
    recuperada = magatzem.carregar_tasques(ruta)
    assert len(recuperada) == 1
    assert recuperada.cercar("Cartell fira").prioritat == "alta"

Tres proves s'han convertit en una que continua executant-se tres vegades, i afegir-hi un quart cas ara costa una línia. La segona prova fa servir tmp_path per escriure en una carpeta temporal: mai no toca el tasques.json real, i la carpeta desapareix en acabar. És una prova d'integració en tota regla, perquè per passar necessiten funcionar Agenda, Tasca.to_dict, from_dict i les dues funcions del magatzem.

Conclusió

Una prova automatitzada és codi que executa el teu codi i comprova el resultat, i el seu valor real és defensar-te de les regressions: errors que apareixen en alguna cosa que abans funcionava. Hi ha proves unitàries, d'integració i de sistema, i la proporció sana és moltes de les primeres i molt poques de les últimes. El que fa provable una funció és exactament el que vam separar a 04-04: rebre dades i retornar valors, sense input() ni print() pel mig. Tota prova segueix el patró AAA —preparar, actuar, comprovar— i porta un nom descriptiu, que és l'única cosa que veuràs quan falli. Amb assert ja es pot comprovar qualsevol cosa, i sobre aquesta base s'aixequen les dues eines: unittest, inclosa a Python, amb classes TestCase, mètodes test_*, assertEqual/assertTrue/assertIn/assertRaises i setUp/tearDown, executada amb python -m unittest discover; i pytest, que s'instal·la amb pip i prescindeix de classes, comprova amb l'assert normal, informa amb detall de quin valor ha sortit i quin s'esperava, i aporta pytest.raises, @pytest.mark.parametrize per executar una prova amb molts jocs de dades, fixtures per preparar el que és comú i tmp_path per provar fitxers sense tocar les dades reals. Es proven els casos normals, els casos límit —on són gairebé tots els errors— i els casos d'error, sempre contra el comportament i mai contra la implementació. La cobertura serveix per trobar buits, no com a objectiu: mesura què s'executa, no què es comprova. I TDD —vermell, verd, refactoritzar— és especialment útil en arreglar un error: primer la prova que el reprodueix, després l'arreglada.

TascaFàcil és ara la v0.21: una carpeta tests/ amb test_model.py i test_agenda.py, vuit proves que s'executen en centèsimes de segon i que ja han trobat un error real —el progrés que superava el 100 %— i l'han deixat arreglat i protegit per sempre. Amb això tens les quatre peces que faltaven en acabar el mòdul 7: documentació, gestió d'errors, historial i proves. Queda una última cosa, i és la que separa un programa que funciona d'un programa del qual un se sent orgullós: com està escrit. Hi ha funcions a interficie.py que han crescut fins a les quaranta línies, números solts repetits per diversos fitxers, noms heretats de quan el programa era un exercici i trossos de codi duplicats. A Estil, llegibilitat i refactorització s'arregla tot això amb PEP 8, amb eines automàtiques i amb un catàleg de refactoritzacions —recolzant-te, precisament, en les proves que acabes d'escriure.

© Copyright 2026. Tots els drets reservats