lunes, 30 de agosto de 2010

Algunas herramientas para usar con Selenium

Como contaba aquí estuve automatizando tests de aceptación con Selenium, utilizando como lenguaje de programación para los tests Ruby. En este post voy a hablar un poco de las herramientas que utilicé para facilitar esta automatización.

La tarea más importante (pero también la más tediosa) en este tipo de automatizaciones es la identificación de los objetos gráficos (botones, cuadros de texto, etc.) con los que queremos interactuar. Si la aplicación fue desarrollada de manera de que todos los objetos tengan identificadores únicos que se puedan predecir con algún tipo de lógica el problema se simplifica bastante, pero en general esto se da solamente si se fueron escribiendo tests de aceptación en paralelo con el desarrollo de la aplicación. En el caso más usual en que escribimos los tests después de que la aplicación está terminada, debemos dedicarle bastante tiempo a buscar maneras de identificar los componentes de la UI (y no solamente debemos identificarlos si no que debemos hacerlo de una manera robusta, que no cause que todos nuestros tests fallen ante pequeños cambios en la estructura)

Existen varios plugins de Firefox que pueden ayudarnos en esta tarea. En primer lugar está Firebug que es un plugin indispensable para cualquiera que desarrolle aplicaciones Web, con muchísimas funcionalidades que exceden el alcance de este artículo. Solo vamos a mencionar que utilizando este plugin podemos identificar cualquier objeto en la pantalla simplemente haciendo click en el despues de seleccionar la herramienta adecuada.

Otros plugins interesantes son XPather y FireFinder. Estos plugins nos permiten probar expresiones CSS y XPath contra la página que queremos testear, para verificar que estén construidas correctamente y que identifiquen a los objetos que necesitamos. Poder hacer esto desde un plugin contra la página viva nos permite darnos cuenta de errores en forma interactiva, sin necesidad de ejecutar todo un test de aceptación para llegar a la parte del código que contiene la expresión que queremos probar.

Otra manera de probar partes de nuestros tests sin necesidad de ejecutar un test de aceptación completo es utilizar la capacidad de Ruby para trabajar en forma interactiva. Para eso es muy útil el shell de Ruby.

AutoIt es una herramienta fundamental para automatización de tests de aceptación en ambiente Windows. Si bien Selenium está perfectamente equipada para interactuar con casi todos los elementos de la interfaz gráfica, tiene problemas con ciertas ventanas, como las confirmaciones de seguridad o los diálogos para el download de archivos. Esto se debe a que esas ventanas no son ventanas HTML si no que son ventanas nativas del sistema operativo. Por lo tanto, cuando necesitamos realizar actividades que incluyan ese tipo de ventana tenemos que recurrir a herramientas más nativas, como AutoIT. Por suerte, se engancha muy bien con Ruby.

Para poder generar datos para los tests, utilicé esta librería Es un port de una antigua libreria de Perl y contiene colecciones de datos para usar en los tests. Por ejemplo, si necesitamos un nombre para un test hacemos Faker::Name.first_name y esto nos devuelve un nombre al azar de la colección de datos interna de la librería.

Por la ausencia de buenos identificadores en la aplicación para la cual estoy escribiendo los tests, tuve que recurrir mucho a expresiones complejas XPath para identificar los objetos. Esto funcionaba perfectamente bien en Firefox, pero cuando probé con Internet Explorer, los tiempos de ejecución crecieron más de 10 veces!!

Esto se debe a que el motor de Javascript de IE es exasperantemente lento. Era un problema, ya que sabemos que los tests deben ser rápidos, así que estuve haciendo algunas pruebas y la solución que encontré fue la siguiente: en vez de pedirle al browser que identifique un objeto en base a una expresión XPath, le pido todo el DOM en formato HTML y realizo la búsqueda XPath en el cliente Ruby que ejecuta los tests. Esta solución es anti-intuitiva, ya que bajarse todo un documento HTML para hacer una búsqueda parece menos eficiente que hacer la búsqueda en el browser, pero el motor XPath de IE es tan lento que conviene hacerlo así. Para realizar las búsquedas en el documento HTML usé la excelente librería Hpricot, del desaparecido _why.

Finalmente, para organizar la corrida de los tests y para tener un output más o menos lindo con el resultado de la ejecución, usé la librería RSPec, que es un framework que normalmente se usa para BDD en Ruby.

miércoles, 25 de agosto de 2010

Code Smells : Obsesión Primitiva

Queridos Chimpances Entrenados, Dice la Enciclopedia Galatica:

Obsesión Primitiva: Es un Smell que se produce cuando uno utiliza tipos primitivos como Strings o Doubles o Ints para representar conceptos que realmente merecen ser representados por clases de objetos.

Es uno de los smells más comunes que podemos encontrar en cualquier código que escribamos o revisemos.

Cuando se trabaja con Objetos, para modelar la estructura interna de una clase uno tiene dos alternativas:


1 - Referenciar a otra Clase de objeto

2 - Utilizar un tipo primitivo.

En general el problema empieza cuando uno decide utilizar un tipo primitivo para modelar un concepto porque se resiste a crear una clase para el mismo porque en ese momento solo sirve para encapsular un simple entero, una String o un par de estos, con nada o muy poco comportamiento extra.

Tipicos ejemplos de esto es cuando uno modela por ejemplo un rango de enteros, como ser, rango de valores válidos para un atributo para un sistema que modele rangos de edades especificos, campos de calle, numero y ciudad para modelar la direccion de un Cliente, etc. . Otro ejemplo es utilizar un int o String para discriminar el "subtipo" o diferentes variaciones de un objeto. Pero el caso emblematico por definición es cuando uno modela sumas de Dinero como un Double para representar el valor, y una String que representa la especie (dolares, pesos, euros, etc.)

Cuando uno sigue trabajando luego de la elección del tipo primitivo al principio siente que ahorro en cantidad de código y clases creadas, y se felicita por ello.

Pronto, no obstante, a medida que el sistema crece comienza a pedir más y más comportamiento de ese tipo primitivo, que, como en general no puede agregarse en el mismo, en lenguajes como Java o C# , se terminan ensuciando el código de la clase adonde se encuentra el primitivo, adonde se agregan métodos y más métodos helpers o utils que trabajan con ese tipo primitivo y con ninguno de los otros atributos de la clase.

Sin entrar en campañas proselitistas, otros lenguajes si permiten agregar métodos no solo a las clases de los primitivos, sino también a instancias específicas (prototype) como por ejemplo en Ruby o Smalltalk, pero no obstante las reglas de Buen Diseño esta indicando la necesidad de un objeto que represente mejor el concepto y que va a permitir agregarle comportamiento a medida que la solución la vaya necesitando (no olvidemos: nunca especular que se necesitará por adelantado).

Ejemplo 1
package ar.com.smells.examples; 
public class ClienteFibertel { 
private String nombre;  
private  String apellido; 
private MedioPago medioPago; 
private long idCliente; 
 
... 
 
private String nombreCalle; 
private String numero; 
private String piso; 
private String Depto; 
private String codigoPostal; 
private String ciudad; 
 
//Metodos propios de clase Cliente 
 
   .... 
 
//Metodos que piden a grito una clase Direccion 
 
    public boolean esCodigoPostalRural() { 
     bla bla bla 
    }
    
    public String direccionFormateadaParaEnvioPostal() {
            bla bla bla 
    } 
 
    public boolean esEdificio() {
           bla bla y mas bla. 
    }
 
} 
En este caso, se deberia refactorizar a una clase Direccion que forme parte del Cliente y que deberia llevarse los tres métodos que estamos indicando que no forman parte de la lógica propia de la clase adonde están.

Ejemplo 2

Un ejemplo muy especial dentro de la obsesion por primitivos, aunque no es el tipico ejemplo, es cuando se utiliza un int como subtipo y esto genera que se repita la lógica de distinción entre subtipos entre todos los métodos de la Clase. La solución es muy clara, la clase debe separarse en una clase base y varias subclases con la diferencia.

 
public class ConexionInternet
{ 
   ... 
 
private int tipoConexion;
 ... 
 
    public String metodoA() { 
         if (tipoConexion == ConexionInternet.CABLEMODEM) { //ya no  hay mas, adios Fibertel
 
         } else if (tipoConexion == ConexionInternet.ADSL) { // speedy? arnet? }
 
           } else if (tipoConexion == ConexionInternet.Modem) { //El unico que va a quedar, la vuelta al 56K! 
 
             }
   } 
 
   public boolean metodoB() {
       if  (tipoConexion == ConexionInternet.CABLEMODEM) { //ya no hay mas, adios Fibertel
     
       } else if (tipoConexion == ConexionInternet.ADSL) { // speedy? arnet? 
 
       
        } else if (tipoConexion == ConexionInternet.Modem) { //El unico que va a quedar, la  vuelta al 56K! 
 
        } 
   }
 
} 

Es bastante lógico decir que parece razonable refactorizar en 3 clases ConexionInternetCableModem, ConexionInternetADSL, ConexionInternetModem, todas dependiendo de una clase abstracta o implementando una interfaz ConexionInternet.

De cualquier manera una de los ejemplos más clásicos y que mayores problemas trae al estar “Obsesionado por Primitivos” es cuando se modelan Sumas de dinero como objetos Double. Los que escribimos este blog damos plena fe de ello.

El primer problema aparece cuando uno comienza a realizar operaciones y surgen las "pequeñas" diferencias por redondeo!. Y claro, cuando uno tiene problemas de redondeo con kilometros entre planetas no hay mucho problema (salvo que este manejando una nave espacial :), pero si es dinero... ahi sii, esos centavos importan y como!

Luego la siguiente estación al desastre es cuando el Usuario, a pesar de habernos dicho que siempre trabajaba con dinero local (pesos, pesos Chilenos, reales, lo que sea) viene muy contentos a contarnos que ahora va a empezar a aceptar pagos en dolares, euros, y en… rupias indias!.

Y nosotros con la clase Producto definida asi:

 
public class Producto {
... 
private double precio; 
 
.... } 

Inmagínense el cambio en todo el sistema!

La solución es ampliamente conocida y no nos vamos a extender en ella por temor a hacer un post kilométrico, pero para los que les interesa pueden verla en detalle en el libro de Test Driven Development (Lo se, lo hemos recomendado muchas veces, de Kent Beck).

Para los ansiosos o remolones, la solucion es crear una clase Money que siga el patrón de diseño Value Object, sin mostrar el detalle de los métodos, sería algo asi:

 
public class Money { 
 
private BigDecimal monto; 
private Currency moneda; 
….
} 

Un hecho relativamente moderno que ha reafirmado e incentivado la Obsesión Primitiva es la utilización másiva de ORMs (Object Relational Mapping), como Hibernate.

El problema en este caso es cuando se esta definiendo un Bean que terminará como tabla en una base de datos y, y condicionados por la complejidad de la herramienta, se prefiere tener un bean al cual le agregan los atributos primitivos directamente y no múltiples tablas de objetos relacionados.

Por ejemplo, prefieren tener una sola clase SujetoAnses al cual le agreguen 2 int para guardar rango máximo y mínimo configurado a tener otra tabla relacionada con el Bean principal con los rangos permitidos.

O del ejemplo anterior, ClienteFibertel con todas las String en una misma tabla a tener una relacion con otra tabla Direccion. Es inútil explicar en estos casos que en general no es razonable decidir el Diseño de Objetos por las consecuencias que tendrán sobre la base de datos. Al menos no a priori hasta tener razones de peso, de performance, etc..

Para terminar, no estamos afirmando que TODO atributo o concepto simple debe ser representado como una clase de objetos que se convierta en simples wrappers de un int, String o double.

Pero ante el minimo comportamiento que haya que agregar, cuando uno comienza a pasar ese valor de aquí para allá o hace algún método que lo utiliza solo a él, etc., en esos casos mantenerlo como primitivo es… una obsesión y hay que tratarla!.

lunes, 16 de agosto de 2010

Test de unidad con Selenium

Hace algunos días que estoy automatizando un conjunto de Smoke Tests, es decir tests que se ejecutan para ver si algo se rompe al hacerlo, sin verificar que el resultado de las operaciones sea el esperado. Estos tests se ejecutaban antes a mano, con el esperable desperdicio de recursos.

Para la automatización decidí usar Selenium, por ser una de las herramienta open-source más populares y porque tiene muy buena portabilidad entre browsers. Selenium es una librería Javascript que se agrega a nuestra aplicación Web (en forma transparente, ya vamos a ver como) y que la extiende con una interfaz capaz de recibir comandos que permiten interactuar con los elementos gráficos de la UI.

Existen varias maneras de utilizar Selenium. La más sencilla es Selenium IDE, que permite crear tests mediante el proceso de "Record & Play", es decir que tiene la capacidad de registrar nuestra interacción con un sitio web y de reproducir luego exactamente las mismas acciones. Esta modalidad no exige saber nada sobre el funcionamiento de Selenium y permite que una persona sin conocimientos de programación pueda escribir sus propios tests. Sin embargo tiene el grave incoveniente de que los tests así creados son muy frágiles: cualquier cambio en la aplicación causa que los tests dejen de funcionar y deben ser creados de nuevo desde cero (este problema no es particular de Selenium IDE si no que es compartido por todas las herramientas que utilizan el esquema "Record & Play").

Debido a estas limitaciones decidí utilizar Selenium Remote Control. Esta solución tiene una arquitectura de cliente/servidor. El servidor se encarga de controlar una instancia de un Web Browser (puede ser Internet Explorer, Firefox, Opera, Chrome y Safari). El cliente interpreta los tests y envía comandos al server, que se ocupa de ejecutar los comandos recibidos en el browser que controla. Esta arquitectura permite al servidor injectar las librerías Javascript que componen el core de Selenium en forma transaparente, sin que tengamos que instalarlas en el servidor de la aplicación que queremos testear como en versiones anteriores.

Como la interfaz entre el cliente y el servidor es HTTP puro, los clientes (y por lo tanto los tests) pueden estar escritos en cualquier lenguaje capaz de ejecutar un request HTTP. Existen clientes para C#, Java, Perl, PHP, Python y Ruby.

Veamos un ejemplo sencillo de como utilizar Selenium RC con Ruby. En primer lugar es necesario realizar la instalación de Selenium. Para el server, solo es necesario bajar y descomprimir este archivo y tener disponible un run-time de Java.

Para instalar el cliente ruby, podemos utilizar el siguiente comando:

gem install selenium-client

Una vez realizada la instalación, hay que poner a correr el server:

cd selenium-server-1.0.3 java -jar selenium-server.jar

Y podemos probar este pequeño script de prueba, que ejecuta una búsqueda en Google:

#hello_google.rb require "selenium/client"

begin @browser = Selenium::Client::Driver.new \ :host => "localhost", :port => 4444, :browser => "*firefox", :url => "http://www.google.com", :timeout_in_second => 60

@browser.start_new_browser_session @browser.open "/" @browser.type "q", "Selenium seleniumhq.org" @browser.click "btnG", :wait_for => :page puts @browser.text?("seleniumhq.org") ensure @browser.close_current_browser_session end

Este script crea una instancia del cliente Ruby y ejecuta los comandos necesarios para escribir la frase "Selenium seleniumhq.org" en el cuadro de texto de google y a continuación presiona el boton de búsqueda.

El código que se ocupa de escribir el texto es un ejemplo muy representativo de como funciona Selenium asi que vamos a utilizarlo para entrar un poco más en detalle.

@browser.type "q", "Selenium seleniumhq.org"

El segundo parámetro del método type es solo el texto a cargar y por lo tanto no es muy interesante. El primer parámetro en cambio es el mecanismo para localizar el objeto en que queremos escribir (locator en la jerga Selenium) y es muy importante, ya que la identificación en forma efectiva y robusta de los distintos componentes de la interfaz es fundamental en estos trabajos de automatización.

En este caso el locator es simplemente el id del text box. Esta forma de identificación es claramente la más sencilla y probablemente la mejor desde el punto de vista de la mantenibilidad. Sin embargo, muchas veces no es posible utilizarla, ya sea por que los objetos que queremos identificar no tienen id o porque el id de un mismo objeto cambia entre sesiones. Para estos casos, Selenium tiene formas más poderosas de identificacion.

Por ejemplo podemos identificar objetos utilizando XPath (http://en.wikipedia.org/wiki/XPath), que es un lenguaje para acceder a las distintas partes de un documento escrito en un lenguaje de markup. Veamos algunos ejemplos de selectores XPATH

Escribe en el elemento de tipo input con el id "q" (equivalente al ejemplo anterior) @browser.type "xpath=//input[@id='q']", "texto a buscar"

Identifica un div con el texto "Hola", pero hace click en otro div, que es "hermano" del identificado @browser.click "xpath=//span[text()='Sales']/../div"

XPATH es muy poderoso y un tutorial completo escapa a los objetivos de este artículo. Pueden encontrar una introducción aquí: http://www.w3schools.com/xpath/xpath_intro.asp . Un detalle importante para la utilización de XPath con Selenium es que no todos los browsers proveen una buena implementación de XPath, por lo que muchas veces conviene pedirle a Selenium que utilice su propia versión de la herramienta, que es un poco más lenta pero más poderosa.

Otra alternativa para identificar objetos con Selenium es CSS. En este caso la forma de nombrar los objetos es exactamente la que utilizamos para describir su presentación en archivos CSS. Veamos algunos ejemplos:

Escribe en el elemento de tipo input con el id "q" (equivalente al primer ejemplo XPath) @browser.type "css=input#q", "texto a buscar"

Click en el link que contenga el texto "Mi Link" @browser.click "css=a:contains('Mi Link')

Una ventaja importante de CSS sobre XPath es que en Internet Explorer corren muchísimo más rápido los comandos que utilizan CSS.

Como conclusión, Selenium me pareció una herramienta muy interesante. Con un poco de práctica se pueden escribir tests con bastante facilidad y esos tests se pueden utilizar después en distintos browsers prácticamente sin modificaciones. En artículos posteriores vamos a hablar de buenas prácticas en la escritura de tests con Selenium y también de la utilización de algunas herramientas para hacer más fácil la detección de formas de identificar los componentes de la UI con los que queremos interactuar.

martes, 10 de agosto de 2010

El camino del Conocimiento: de Aprendiz a Maestro

El problema en cuestión es: Como encarar la adopción de una metodología ágil?

Artes Marciales y un poco de historia

Como siempre, la cultura milenaria Japonesa viene en nuestra ayuda. Existe un concepto dentro del arte marcial japonés llamado ShuHaRi, que describe, dentro del camino hacia el conocimiento, 3 etapas necesarias para volverse un maestro:

ShuHaRi
Es la composición de tres palabras: Shu - Ha - Ri

Shu significa "proteger" y "obedecer", con significado de obedecer el "conocimiento tradicional". El aprendiz debe aprender las técnicas y conocimientos fundamentales, bajo la supervisión de un maestro. Todavía no esta listo para explorar y comparar diferentes caminos.

Ha significa "desprenderse", "desamarrar", "derivar". Es romper con la tradición. De una manera controlada el "Practicante" comienza a explorar diferentes caminos.

Ri es "dejar", "separarse", "trascender". Ya no hay un seguimiento de reglas o técnicas porque todo movimiento es natural y fluido. El conocimiento forma ya parte indivisible con la persona que se ha transformado finalmente en un Maestro.

Toda persona que comienza a aprender este arte marcial debe pasar por estas tres etapas que lo llevan desde aprendiz a Maestro.

En la cultura occidental , durante la edad media los aspirantes a determinados oficios (herrero, carpintero, etc.), recorrían un camino similar a ShuHaRi, adonde cada aprendiz empezaba a trabajar con un Maestro, con el tiempo se convertía en un profesional, practicante de su oficio y luego de muchos años y una mejora constante llegaba a tomar el lugar de Maestro y completaba el ciclo teniendo sus propios aprendices.

Adopción de metodologías ágiles

Desde la irrupción masiva de las metodologías ágiles en el mundo del desarrollo de software y su llegada a nuestro país hemos presenciado varias veces el momento en el cual una empresa comienza a aplicar ágiles.

Ignoramos si solo es una característica de nuestro país, o es general pero observamos la misma falla una y otra vez:

Se define el cambio a una nueva metodología, tomemos con ejemplo a Scrum, se capacitan a las personas, se contrata a coachs, se eligen equipos, etc., pero se comienza a aplicarlo con modificaciones a la esencia de la metodología. Es decir, se saltean la etapa imprescindible del aprendizaje, la de Aprendiz (Shu) y pasan directamente a Ha.

Por supuesto, estas modificacione se producen esgrimiendo variadas y al parecer muy validas razones, respaldadas por las excusas más variadas, como por ejemplo:

  • "En esta empresa hacemos las cosas diferentes".
  • "No podemos seguir la metodología al detalle, acá somos muy especiales".
  • "El de Product Owner es un concepto de lo más interesante pero... acá los usuarios son difíciles. No va a servir."
  • Aca los usuarios están muy ocupados.
  • No, aquí esto va a traer problemas (así, de una manera difusa, en voz baja, con miradas nerviosas por sobre el hombro).
  • No se adapta a la cultura de la empresa.
  • Este principio no aplica a nuestro entorno porque va en contra de nuestra politica X.
  • etc.

Se plantean así desde el primer momento "adaptaciones" a la metodología. Adaptaciones estas que son cambios encubiertos, que de una u otra manera van en contra de los principios por los cuales la metodología funciona.

Es decir, se busca la excusa que sea necesaria para cambiar para no cambiar. Se hace cualquier modificación de la forma de trabajar que no implique un cambio real en la forma de trabajar.

Porque justamente todo cambio Real implica entrar en una zona de incomodidad, de no confort y eso generalmente cuesta, cuesta mucho.

Vale una aclaración importante, no estamos proponiendo no hacer ninguna adaptación a la realidad en la que nos movemos, a la cultura adonde estamos inmersos y ser talibanes al imponer rigurosamente cada punto y coma de la metodología en cuestión. Cuestiones acerca de si entrar de lleno con todas las prácticas o el grado de profundidad que aplicará desde entrada, si incluir hasta la ultima práctica recomendada de entrada o ir haciendolo de manera progresiva, la forma o detalles de implementación de las prácticas técnicas, etc, son todos puntos a pensar y adaptar de la mejor manera para el contexto y proyecto en cuestión.

Pero, el punto esencial es que si uno eligió una metodología ágil no puede proponer cambios que vayan en contra de los principios y valores sobre los cuales esta basados esa metodología. Así de sencillo.

El camino del aprendizaje

Miremos, por otra parte, como propone encarar la adopción de una metodología ágil, James Shore, un muy conocido evangelizador de metodologías ágiles, fundamentalmente de XP, y autor del libro "The Art of Agile Development".

En su "Camino del aprendizaje" justamente basado en ShuHaRi propone encarar el estudio de un método nuevo, como es, adoptar una nueva metodología de desarrollo, de la siguiente manera:

1 - Seguir las reglas: Esta es la etapa inicial durante la cual se elige un metodo y se lo sigue tan rigurosa y detalladamente como sea posible. Durante esta etapa uno esta aprendiendo del tema en cuestion, asi que debe estudiar, debe seguir el metodo, reflexionar sobre la experiencia y volver a estudiar. Es importante justamente seguir las reglas fielmente, en su espiritu al menos, para poder evaluar si nos sirve o no. En lo que respecta a este tema uno es un aprendiz.

2 - Romper las reglas: En esta etapa, habiendo uno realmente aprendido el metodo (sin modificaciones "tempranas") y haberlo practicado durante un cierto tiempo se plantea una etapa de mejora basada en pequeños cambios controlados. Uno debe evaluar los resultados obtenidos, plantear un solo cambio y volver a prácticar el metodo completo, para poder medir la diferencia del cambio realizado. Durante esta etapa uno ya ha pasado la etapa de aprendiz y sabe tanto del método como para poder ajustar determinadas partes sin ir en contra de los principios que lo hacen funcionar. Es ya un Practicante del método.

3 - Ignorar las reglas: Por último en esta etapa el conocimiento se incorporó completamente y las acciones a realizar surgen naturalmente respetando fielmente los principios y valores subyacentes. Uno no debe pensar ni recordar ya en las reglas de manera individual porque las tiene internalizadas de manera que se aplican constantemente pero adaptandolas instantaneamente al problema en cuestión. Se hace lo que le parece correcto basándose en su amplia experiencia y el conocimiento del método que absorbió y luego se observa y actúa en base al resultado.

El Practicante se ha convertido en Maestro.

Nota de los autores: Queremos agradecer a James Shore por su permiso para traducir, publicar y discutir su poster "camino del conocimiento, así como también su excelente predisposición para ayudarnos. Recomendamos fuertemente cualquier libro o artículo proveniente de Él.

Author's note: We would like to thanks James Shore for his permission to translate, post and discuss his poster "The Road to Mastery" and for his help and feedback. We strongly recomend any of his books or articles.

lunes, 2 de agosto de 2010

Code Smells

De chiquitos aprendemos que hay que evitar lo que tiene mal olor. La madre que huele el pañal de su hijo sabe que si apesta tiene que cambiarlo, y si apesta mucho, mucho, quizás el bebe tenga algún problemita de diarrea o algo similar. Sentido común universal este que sin embargo no aplicamos frecuentemente en el desarrollo de software.

A lo largo de la corta pero no menos exitosa vida de este Blog hemos mencionado varias veces a los Code Smells, sin aclarar su significado, mea culpa, porque a veces damos por sentado conceptos que son muy útiles. Este post entonces intenta corregir semejante falta:

Code Smells: (Aplicado al arte de la programación) son todos los síntomas que podemos encontrar en el código fuente de un sistema que indican que muy probablemente existan problemas más profundos de calidad de código, de diseño o de ambos.

El termino fue acuñado por Kent Beck y Ward Cunninghan hacia fines de los 90 y su uso se popularizó a partir del libro de Martin Fowler y otros: "Refactoring, Improving the Design of the existing Code" .

Si pensamos las barbaridades que hemos visto en algunos códigos fuentes (siempre de compañeros, nunca nuestros... :O ) y asociamos esa sensación de horror y rechazo con el olor a podrido de algunos ríos contaminados (Nota para los argentinos: Iba a poner Riachuelo pero no se si el resto del mundo conoce la fama "olorosa" de nuestro querido río, querido porque esta en la Boca ;), decía, esa imagen gráfica claramente lo que significa Code Smell, síntoma de que algo Apesta, y como al pañal, hay que cambiarlo.

En la práctica, históricamente, los Code Smells se han usado para identificar partes del código de baja calidad a las que aplicar las técnicas de refactoring explicadas en el libro arriba mencionado. Es decir, el libro describe un conjunto largo de distintas técnicas de transformación de código sin alterar su significado, las asi llamadas "Técnicas de Refactoting" y los Code Smells son los que indican en que parte del código hay que revisar como candidatas para aplicar esas técnicas.

No es la intención de este post describir uno a uno los Code Smells porque eso llevaría demasiada "tinta virtual" pero prometemos ir describiendo los Smells principales ya que consideramos (y bueno, no seria nada nosotros pero también lo piensa la gente más brillante de nuestro campo de la programación) que es uno de los conceptos fundamentales de la programación de estos tiempos, de los más aplicables y útiles, en combinación con los tests de unidad y que mayor impacto tienen tanto para la productividad como para la calidad de los sistemas que generemos.

Los principales Code Smells que se pueden encontrar en el código son :

  1. Código Duplicado
  2. Parámetros Booleanos
  3. Métodos Largos
  4. Clases Largas
  5. Larga Lista de Parámetros
  6. Cambio Divergente
  7. Shotgun Surgery (traducido algo asi como "cirugía a escopetazos")
  8. Feature Envy (textualmente: "envidia de features")
  9. Data Clumps (algo asi como aglomeracion de datos, masa de datos, en sintesis: un bodoque de datos (Arg))
  10. Obsesión por primitivos
  11. Generalización especulativa
  12. Campos temporales
  13. Refused Bequest ( algo asi como Rechazo a recibir parte de una herencia)
  14. Etc. (porque hay muchos mas).

Sigan sintonizando nuestro canal http://trainedchimpanzees.blogspot.com , porque vamos a estar analizando los mismos en sucesivos posts. De hecho ya empezamos a describir los Code Smells, uno de los primeros posts que hicimos fue sobre el Smell "Parametros Booleanos".

Para terminar una frase atribuida a la abuela de Beck, tómenla como consejo:

"Si apesta, hay que cambiarlo!"