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
- Per què provar
- Tipus de prova
- Què fa provable un codi
- El patró AAA i el nom del test
assert: la primera provaunittest, la biblioteca estàndardpytest, l'eina de l'ecosistemaunittestdavant depytest- Què provar i què no
- Cobertura
- TDD: vermell, verd, refactoritzar
- TascaFàcil v0.21: la carpeta
tests/ - Errors habituals i consells
- Exercicis
- Conclusió
- 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_diesexplica 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ó.
- 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.
- 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.
- 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:
- Arrange (preparar): crear les dades i els objectes que necessita la prova.
- Act (actuar): executar exactament una cosa, la que s'està provant.
- 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 == 4I 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à.
assert: la primera prova
assert: la primera provaAbans 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.
unittest, la biblioteca estàndard
unittest, la biblioteca estàndardunittest 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 sí 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
pytest, l'eina de l'ecosistema
pytest, l'eina de l'ecosistemapytest 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.
# 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() == esperatTres 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).
unittest davant de pytest
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.
- 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 retornarNoneen 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.
- Cobertura
La cobertura mesura quin percentatge de les teves línies executen les proves. S'obté amb una eina externa:
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.
- 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.
- TascaFàcil v0.21: la carpeta
tests/
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.pyEls 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.06sLa 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()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()iprint(). 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
setUpi les fixtures. - Tocar les dades reals. Una prova que escriu a
tasques.jsont'esborrarà la feina algun dia. Fes servirtmp_path. - Provar la implementació, o posar noms com
test_1itest_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.25El 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 NoneLes 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.
Fonaments de la Programació
Mòdul 1: Introducció a la Programació
- Què és la programació?
- Història de la programació
- Llenguatges de programació
- Entorns de desenvolupament
- Del problema a l'algorisme
Mòdul 2: Conceptes Bàsics
- Variables i tipus de dades
- Operadors i expressions
- Entrada i sortida de dades
- Conversió de tipus i validació de dades
Mòdul 3: Estructures de Control
Mòdul 4: Funcions i Procediments
- Definició i ús de funcions
- Paràmetres i retorn de valors
- Àmbit de variables
- Descompondre un programa en funcions
- Funcions com a valors: lambda i ordre superior
Mòdul 5: Estructures de Dades
- Llistes i arrays
- Cadenes de caràcters
- Diccionaris i conjunts
- Tuples i estructures imbricades
- Desar dades en fitxers: text, CSV i JSON
Mòdul 6: Algorismes Bàsics
Mòdul 7: Objectes i Organització del Codi
- De les dades als objectes: classes i instàncies
- Atributs, mètodes i constructor
- Col·leccions d'objectes
- Mòduls, paquets i importacions
Mòdul 8: Bones Pràctiques i Eines
- Documentació i comentaris
- Depuració i gestió d'errors
- Control de versions
- Proves automatitzades
- Estil, llegibilitat i refactorització
