Documentació i test de programes

Documentació de programes

Documentar un programa significa escriure un text o conjunt de textos que permeten a una persona conèixer millor el programa en algun dels seus aspectes. Segons a quin perfil de persona va dirigida la documentació parlem de:

  • Documentació d'usuari: quan la documentació va dirigida a les persones que han d'utilitzar el programa. Aquest tipus de documentació ha de ser planera i explicar com es fa servir el programa i com, amb el programa, es poden dur a terme certes tasques.
  • Documentació tècnica: quan la documentació va dirigida a les persones que han de mantenir, ampliar o refer el programa. Aquesta mena de documentació va dirigida a persones de perfil tècnic i explica com està estructurat el programa, què fa cadascuna de les parts, etc.

Per il·lustrar els dos tipus de documents, observeu les dues referències següents, que pertanyen a la documentació de l'OpenOffice.org:

En aquest curs únicament ens preocuparem de la documentació tècnica.

Documentació Tècnica

La documentació tècnica la formen totes aquelles notes i textos que descriuen com és i com està estructurat un programa, com són les dades que manipula i altres informacions destinades a facilitar la comprensió del programa als tècnics que en tenen cura. Un problema important relacionat amb aquesta informació és el seu manteniment. El problema és senzill d'entendre: els programes evolucionen, es modifiquen, refan, etc. Mantenir la documentació tècnica significa mantenir la coherència entre el programa i la seva documentació per tal que continui essent útil.

Un altre element important en la documentació tècnica el constitueixen els comentaris en el codi. Recordeu que els comentaris en el codi són textos que es poden escriure en mig d'un programa, que no formen part del programa però que permeten deixar-hi anotacions que en facilitin la comprensió.

Com comentar un programa

Comentar un programa és una activitat que fa el mateix programador a mida que va escrivint codi. Tal i com hem dit abans, l'objectiu és facilitar la comprensió del programa a qui se'l llegeixi. Per a comentar correctament un programa cal tenir en compte les següents regles:

  • Sigueu breus, precíssos i escasos en comentar un programa. L'abundor o el volum dels comentaris no fan un programa més fàcil d'entendre.
  • No expliqueu mai en un comentari allò que resulta evident en llegir el codi.
  • Documenteu la interfície (què fan i com s'usen) de les funcions. Aquesta regla s'aplica també a mòduls, clàsses i altres construccions del llenguatge similars.
  • Documenteu com s'emmagatzema la informació si no resulta evident en llegir el codi.

Exemple 1

Observeu el següent programa:

# programa per calcular la suma dels  200 primers
# nombres enters. Usa una iteració i va acumulant sobre
# la variable s la suma parcial.
n = 0
s = 0
while n < 200:
s = s + n
n = n + 1
print s

Com ja haureu endevinat l'estil dels comentaris no és gens adeqüat:

  • És excessivament comentat.
  • No és prou precís: quins són els 200 primers enters? Els que van de 0 a 199 o els que van de 1 a 200?
  • Explica detalls que són obvis si es llegeix el codi:  fixeu-vos on diu "usa una iteració...". Resulta obvi que el programa usa una iteració si es llegeix, oi?

Aquest programa estaria més ben comentat si oferís aquest aspecte:

# Calcula la suma dels enters entre 0 i 199
n = 0
s = 0
while n < 200:
s = s + n
n = n + 1
print s

Exemple 2

El següent programa conté una funció. Observeu com, emprant comentaris, s'ha especificat la funció.

# Retorna cert ssi l'enter n>0 és primer
def es_primer(n):
if n < 3:
return True
else:
i = 2
while i * i <= n and n % i != 0 :
i = i + 1
return n % i != 0

v = input('Valor? ')
print es_primer(v)

Docstrings

Una funcionalitat molt específica de Python és la dels docstrings. Un docstring és un string (text) que situat en certs contextos d'un programa actua com a comentari. En el cas de Python, els docstrings poden usar-se (i és recomanable fer-ho) per comentar funcions i mòduls. Per exemple, la funció de l'exemple 2, seria més correcte haver-la comentat usant un docstring com segueix:

def es_primer(n):
""" Retorna cert ssi l'enter n>0 és un primer """
if n < 3:
return True
else:
i = 2
while i * i <= n and n % i != 0 :
i = i + 1
return n % i != 0

v = input('Valor? ')
print es_primer(v)

Noteu que els docstrings només poden anar en certes posicions fixes del programa i s'acostumen a escriure entre triples cometes. No és correcte, per exemple, escriure coses com:

def es_primer(n):
""" Retorna cert ssi l'enter n>0 és un primer """
if n < 3 :
return True
else :
""" Cerca un divisor de n """
i = 2
while i * i <= n and n % i != 0 :
i = i + 1
return n % i != 0

v = input('Valor? ')
print es_primer(v)

Així doncs,  el segon comentari s'hauria d'escriure usant el prefix # com haviem vist anteriorment.

També és corrent usar docstrings al principi d'un mòdul per descriure la seva funcionalitat. Imagineu-vos que el fitxer divisors.py conté algunes funcions per treballar amb aritmètica entera. El seu aspecte podria ser el següent:

"""
Funcions bàsiques per treballar amb múltiples i divisors d'enters.
"""

def menor_divisor(n):
"""
Retorna el divisor d'n més petit descomptat el 1
"""
...

def es_primer(n):
""" Retorna cert ssi n>1 és un primer """
...

Els docstrings són processats i emmagatzemats per l'intèrpret de Python i poden ser consultats després amb la comanda help. Per a comprovar-ho, obriu l'intèrpret de Python i feu:

help(max)

Obtindreu un petit text explicatiu sobre la funció max.

Ara escriviu en l'intèrpret la funció es_primer que hem usat abans. Després executeu la següent comanda:

help(es_primer)

El resultat serà quelcom semblant a:

es_primer(n)
Retorna cert ssi l'enter n>0 és un primer

Els docstring poden ser usats per aplicacions generadores de documentació tècnica. Aquestes aplicacions prenen com a dades un programa documentat i generen com a resultat una pàgina web o un document en pdf que conté la documentació tècnica del programa. Usant aquestes eines, el problema de mantenir la coherència entre la documentació tècnica i el programa se simplifica notablement.  Pydoc, epydoc o sphinx són alguns generadors de documentació que poden usar-se per a programes Python.

Correctesa dels programes

Un dels grans problemes recurrents en l'àmbit de la programació és el de la correctesa d'un programa (o d'una part d'un programa). Òbviament és un objectiu de tot programador escriure programes correctes. Un programa és correcte quan per a totes les dades possibles acaba i ho fa amb el resultat correcte. Prenem com a exemple la següent funció:

def suma_n(n):
    """ Calcula la suma dels enters entre 0 i n (inclosa) """
    i = n
s = 0
    while i != 0:
s = s + i
        i = i - 1
    return s

Per veure si és correcte, primer cal conèixer el domini del programa i.e.: quines són les dades vàlides per les quals pot realitzar-se el càlcul? En aquest cas, i d'acord amb l'especificació de la funció, ho són tots els enters. Provem doncs quin resultat calcula la funció per alguns enters:

DadaResultatCorrecte?
4 10 ok
34 595 ok
-2 no acaba NO

Per a la darrera prova, el resultat hauria d'haver estat (-2)+(-1)=-3. En canvi, la funció no acaba. La funció, per tant, és incorrecta. Fixeu-vos que hem estat molt de sort ja que casualment hem provat un cas erroni i hem pogut concloure que la funció no és correcta. Noteu que quan fem proves, només podem estar segurs que un programa és correcte si funciona per a totes les dades possibles. En general, provar totes les dades possibles és inviable i, per tant, assegurar la correctesa d'un programa mitjançant proves és impossible. En canvi, si descobrim una dada per la que el programa no funciona, podem assegurar que és incorrecte.

Test de programes

Tot i el que s'ha dit a l'apartat anterior, provar els programes (o les parts) amb alguns casos continua tenint interès per que:

  • Ajuda a detectar casos de malfuncionament.
  • Si prenem nota dels casos provats i dels resultats, es complementa la documentació tècnica atès que els casos provats serveixen d'exemples d'ús del programa, funció, etc.
  • Si tenim una bateria de tests (casos de prova) prou significativa i la comprovem de manera sistemàtica cada vegada després de modificar el programa, tindrem una certa garantia de que el programa modificat no ha deixat de calcular correctament coses que ja calculava bé abans de la modificació. Tot i que pugui semblar extrany, és habitual que quan es modifica un programa per solucionar un problema o per millorar-ne una funcionalitat, el programa deixi de fer coses que ja feia correctament. Això es coneix com a software regression.

Per tal que el test de programes sigui efectiu és necessari disposar d'un sistema que faciliti al programador escriure els tests i comprovar-ne la seva execució. En el context de Python, entre altres tècniques existeix la del doctest.  Aquesta tècnica consisteix a escriure els tests en la documentació i, particularment en els docstrings del codi. Per exemple, La funció es_primer podriem acompanyar-la de tests fent:

def es_primer(n):
 """
Retorna cert ssi l'enter n>0 es un primer

>>> es_primer(3)
True
>>> es_primer(12)
False
>>> es_primer(2)
True
"""
if n < 3 :
return True
else:
i = 2    
while i * i <= n and n % i != 0 :
i = i + 1
return n % i != 0

Fixeu-vos que en el docstring de la funció s'han afegit unes línes (que recorden la consola de l'intèrpret de Pyhton). Aquestes línies son casos de prova i indiquen què hauria de passar si s'invoca la funció. Per exemple, quan diem

>>> es_primer(3)
True

Estem indicant que, la funció es_primer hauria de tronar True si s'invoca amb argument 3.

Suposeu que la funció anterior l'heu escrita en el fitxer primer.py i que voleu comprovar que els tests passen correctament. Llavors, el que heu de fer és executar la següent comanda des de la shell de Unix:

nosetests --with-doctest primer.py

Aquesta comanda, executa tots els tests que estan continguts en el fitxer primer.py i escriu  per  la pantalla el resultat:

sebas@picasoques:~$ nosetests --with-doctest primer.py 
.
----------------------------------------------------------------------
Ran 1 test in 0.006s

OK
sebas@picasoques:~$ nosetests --with-doctest primer.py
.
----------------------------------------------------------------------
Ran 1 test in 0.006s

OK

en cas que hi hagi un o més casos erronis, la comanda ho detecta i n'informa.  També es poden escriure tests en el docstring que documenta el mòdul, en el principi del fitxer. La comanda nosetests els comprova tots sistemàticament.

És possible que en el vostre sistema no tingueu instal·lada l'eina nosetests. En aquest cas, tant si esteu en Debian o en Ubuntu la podeu instal·lar simplement executant aquesta comanda en la terminal:

sudo aptitude install python-nose

Test driven development

El test driven development és una tècnica de desenvolupament de programes en que els test tenen un paper preponderant. La idea, breument,  consisteix a escriure primer els tests que un programa (o part de programa) ha de satisfer i després escriure el programa. En certa forma, el programador ha de desenvolupar un programa "lluitant" contra uns tests que ja existeixen fins que el seu programa aconsegueix passar-los. Aquesta tècnica ofereix certes avantatges:

  • L'obligació d'escriure els tests a priori obliga a determinar què ha de fer el programa (o part de programa) abans de començar a dissenyar-lo. Tot i que pugui semblar mentida, un dels problemes habituals en el desenvolupament de software és que els programadors programen sense saber amb precissió que ha de fer el codi que estan escrivint.
  • Queda una bateria de tests que pot ser usada tant durant el desenvolupament com a documentació tècnica.

Si bé de manera simplificada, en aquest curs seguirem aquesta metodologia de desenvolupament.