Número 62
12 de xullo de 2023
|
Unha das miñas teimas cando reviso código é o das funcións que son públicas, ou visibles, só co obxecto de poder chamar por elas nos tests unitarios.
Hai un tempo, Brais, que estaba a traballar na habitual aplicación de xestión de clientes, escribiu unha clase adicada a gravar os datos dun cliente na base de datos e envioume o commit para que llo revisara. Como era de esperar, ese commit incluía a propia clase e os seus tests unitarios.
Esa clase tiña unha habilidade moi especial: era capaz de descompoñer o enderezo postal do cliente para separalo na rúa, número, portal, piso, etc. e gravar os distintos compoñentes en campos distintos da base de datos (xa falei o outro día de se iso me parecía boa ou mala idea, pero neste caso, como era un requirimento do proxecto, os meus sentimentos eran irrelevantes).
Normalmente, a función que descompoñía os enderezos sería unha función privada, xa que só recibía chamadas de outras funcións da mesma clase; porén, como era tan complicada, Brais queríase asegurar de que funcionaba ben. E para se asegurar diso, escribiu uns poucos tests, e para podelos escribir, fixo pública esa función.
Ben se esforzou o home, que incluso lle puxo unha anotación “@VisibleForTesting
” para que a xente que lera o código (ou sexa, eu) soubera por que esa función era pública no canto de privada. O malo é que, con anotación ou sen ela, esa clase de cousa non me chista moito.
Eu teño a firme opinión de que os tests unitarios deberían utilizar só a interface pública do módulo que proban. Ou sexa: non deben ser tests de “caixa branca” nos que o test mete as súas gadoupas nas interioridades do módulo para comprobar que todo o mecanismo traballa como se espera, senón que deberían ser tests de “caixa negra” nos que o test comproba que, cando chama a unha función desta maneira, o módulo produce o resultado esperado.
A única excepción que acepto é a dos construtores: como moitos tests unitarios falsean as dependencias do código que proban, ás veces é necesario construír as instancias das clases dunha maneira especial, e para iso hai que expoñer construtores específicos para os tests. Acéptoos aínda que me fagan doer o corazón.
O que non admito é escribir tests específicos para unha función que debía ser privada. Se é privada, non é externa (por definición) e o test unitario non ten por que saber que existe, e moito menos chamala directamente.
Ás veces, como Brais, alguén pensa que ten a escusa (“a razón”, din eles) perfecta: a operación desta función é moi complicada e é necesario escribir tests para ela. Podería facer tests que a exerciten dunha maneira e da outra a través da interface pública, pero logo os tests serían innecesariamente longos e difíciles de ler, e eu coñézote ben, Jacobo, e sei que aborreces iso, así que velaquí o dilema, Jacobo, escolle: tests simples que chaman á función que debía ser privada, ou tests que non a chaman directamente pero son moi complicados?
A resposta, obviamente, é: se a función desa clase privada é tan complexa que vedes a necesidade de escribir tests unitarios para ela, convertédea nunha “unidade”. É dicir: extraédea a un módulo ou a unha clase na que esa función sexa pública, e despois escribide tests unitarios para ese módulo ou clase.
Iso é o que fixo Brais e, despois diso, decatouse de que os tests unitarios separados eran máis simples e, polo tanto, podían ser máis exhaustivos con menos código. E, coa lección aprendida e aplicada, aprobei o commit.
E despois ocorreu o incidente co framework de inxección de dependencias, pero xa non queda sitio na Folla de hoxe.
A ilustración desta Folla procede dun gravado de Thomas Rowlandson.
Anterior: “Os cabos” | Índice | Seguinte: “Seguridade polo descoñecemento” |
1 comentario