Carlos Ble

Carlos Ble

I am a professional software developer, I solve problems.

I also teach and mentor developers to build better software.

Developing software since 2001.

Can I help you?

  • Do you need high quality tailor-made software?
  • Need training on TDD, clean code or refactoring?
  • Do you need a technical consultant?
  • May I pair with you to write better code?

Events

Upcoming training courses:

  1. TDD - [en Español] - 6 y 7 Octubre
    Gran Canaria
  2. TDD - [in English] - October 20, 21 & 22
    London, UK
  3. TDD - [en Español] - 29, 30 y 31 de Octubre.
    Madrid, Spain

Conferences:

  1. I'll be at the Agile Testing Days 2014
  2. I'll be at the London Test Gathering Workshops.

Archive for January, 2009



Continuous Integration on Python

Hudson and Cruise Control are very popular among Java/.Net developers but when it comes to Python, ...
Hudson also seems to work on python!. Open source tools for Python continuous integration:

However, I just want a simple way to check out the code and run the tests. A Bash script and the Cron daemon is a good solution so far (last script update on Wed 25 Feb 2008):

  1.  
  2. cd /root/siga/src
  3. export DJANGO_SETTINGS_MODULE="settings"
  4. export PYTHONPATH=":/root/siga/src"
  5.  
  6. mailTo="developer1@sample.com, developer2@whatever.com"
  7.  
  8. analize_tests_output ()
  9. {
  10. grep "FAIL:" $1 > failResults
  11. if [[ $? == 0 ]]
  12. then
  13. echo "There are tests failing!:" > tmp
  14. cat failResults >> tmp
  15. mail -s "Continuous integration report" $mailTo < tmp
  16. fi
  17. grep "ERROR:" $1 > errorResults
  18. if [[ $? == 0 ]]
  19. then
  20. echo "There are tests with errors!:" > tmp
  21. cat failResults >> tmp
  22. mail -s "Continuous integration report" $mailTo < tmp
  23. return
  24. fi
  25.  
  26. rm -rf $1
  27. }
  28.  
  29. are_there_changes()
  30. {
  31. svn up > tmpsvnup
  32.  
  33. grep "^U" tmpsvnup 2> /dev/null > /dev/null
  34. if [[ $? == 0 ]]
  35. then
  36. return 1
  37. fi
  38.  
  39. grep "^A" tmpsvnup 2> /dev/null > /dev/null
  40. if [[ $? == 0 ]]
  41. then
  42. return 1
  43. fi
  44.  
  45. grep "^D" tmpsvnup 2> /dev/null > /dev/null
  46. if [[ $? == 0 ]]
  47. then
  48. return 1
  49. fi
  50.  
  51. grep "^G" tmpsvnup 2> /dev/null > /dev/null
  52. if [[ $? == 0 ]]
  53. then
  54. return 1
  55. fi
  56.  
  57. return 0
  58. }
  59.  
  60. are_there_changes
  61. changes=$?
  62. if [[ $changes == 1 ]] # there are svn changes
  63. then
  64. /etc/init.d/wservices stop
  65. sleep 2
  66. /etc/init.d/wservices start
  67.  
  68. # running unittests tests and mocker tests
  69. python tests/runTests.py >> tmptest1 2>> tmptest1
  70. analize_tests_output tmptest1
  71. # runnin django tests
  72. python manage.py test core 2>> tmptest2 >> tmptest2
  73. analize_tests_output tmptest2
  74. fi
  75.  

The cron file:

  1.  
  2. # m h dom mon dow command
  3. 49 * * * * root /root/siga/src/integrator.sh
  4.  

TDD Efficacy and enemies

Part of this information has been got from the testdrivendevelopment group.
The main enemy of TDD is.... the lazy developer. I'd rather say, the lazy programmer. It's good to read that it is just not only me who thinks that laziness is the most dangerous enemy for TDD. If you have a team with those typical guys that don't like their job, you have a problem. You will not make those guys embrace TDD ever. If you work with a group of developers that enjoy coding you can show them the efficacy of TDD from a couple of papers:

The quotes at the first article are great:
We found that test-first students on average wrote more tests and, in turn, students who wrote more tests tended to be more productive. We also observed that the minimum quality increased linearly with the number of programmer tests, independent of the development strategy employed.
The external validity of the results could be limited since the subjects were students. Runeson compared freshmen, graduate, and professional developers and concluded that similar improvement trends persisted among the three groups. Replicated experiments by Porter and Votta and Höst et al. suggest that students may provide an adequate model of the professional population.
We believe that the observed productivity advantage of Test-First subjects is due to a number of synergistic effects:

  • Better task understanding
  • Better task focus
  • Faster learning
  • Lower rework effort

A bunch of cool links

What I've got browsing today:

Pensando en Generics

Dar el salto a los generics de .Net 2.0 supone un cambio en la forma de pensar. Como dicen en inglés, un "mind shift". En este artículo voy a tratar de explicar los generics mediante reglas que hagan fácil el proceso de detectar cúando se pueden usar y de qué manera. Si bien es cierto que generics no es ya nada nuevo, aún hay mucha gente que sigue con .Net 1.1. Ahora que están trabajando en .Net 4.0, es buen momento para irse actualizando.

Me voy a basar en el código fuente de mi propio frameworks DesktopRails y en ejemplos del framework CSLA.

  • ¿Qué son los Generics?

Generics es algo así como el equivalente en .Net a las templates de C++, con unas cuantas diferencias. En realidad son más potentes puesto que el compilador de C# es más avanzado. Cuando vemos un código que usa los símbolos "<>" como el siguiente, sabemos que se trata de generics:

  1. List<int> numbers = new List<int>();
  • Generics para colecciones

El uso más claro de generics es en colecciones de elementos. Con .Net 1.1 usábamos la clase ArrayList para disponer de arrays de elementos de cualquier tipo, puesto que ArrayList permite introducir objetos de tipo System.Object y en .Net todos los objetos heredan de esa clase. Entonces si necesitamos una lista de cadenas haríamos lo siguiente:

  1. ArrayList myList = new ArrayList();
  2. myList.Add("this is a test");
  3. string firstItem = (string)myList[0];

En éste código vemos cómo se introducen elementos en el vector y cómo se extraen. Internamente el runtime toma el elemento como un System.Object aunque es un string, haciendo una conversion que llaman "boxing", empaquetado. A la hora de extraer el elemento y poderlo manipular, el runtime hace "unboxing" cuando se lo indicamos con la versión de tipo (typecast). Esto tiene dos desventajas:

  1. La primera es el rendimiento. Se ha demostrado que la eficiencia es muchisimo mayor con generics porque como veremos a continuación, evitan el boxing-unboxing.
  2. La segunda es que podemos cometer errores que sólo se descubrirán en tiempo de ejecución. Si el lugar donde el elemento se introduce en el vector está lejos del lugar donde se extrae, el programador podría intentar hacer el typecast a un tipo que no es el correcto. El compilador no puede detectar ese error y es en tiempo de ejecución cuando se produciría la excepción.

Con generics existen clases en System.Collections.Generic que sirven para el mismo propósito. Esta sería la alternativa al código anterior:

  1. List<string> myList = new List<string>();
  2. myList.Add("this is a test");
  3. string firstItem = myList[0];

Si en lugar de declarar firstItem como string hubiesemos puesto int, el compilador hubiese dado un error. Además, VisualStudio y MonoDevelop (IDEs) son capaces de usar autocompletado  o intellisense para recordarnos que el tipo es string, con lo cual es posible que nisiquiera lleguemos a cometer el error.

Forma de detectar el uso de generics: Cada vez que tenemos una colección y estamos haciendo un typecast, podemos darnos cuenta de que se puede reemplazar ese código por una colección generica.

  • Generics para relaciones fuertemente tipadas

Ahora vamos a ver usos más complejos de generics, aunque en realidad cuando se adquiere esta forma de pensar, es fácil de escribir las clases genericas. Usemos como ejemplo una clase Presenter, que se encarga de pintar vistas en la pantalla. Las vistas son de tipo IMyView. La relación entre Presenter y las instancias de IMyView es clara. Si no tuviésemos generics el código sería algo como esto:

  1. public interface IMyView
  2. {
  3. string BackgroundColor {get; set;}
  4. }
  5.  
  6. public class SomeView : IMyView
  7. {
  8. private string _bgcolor = String.Empty;
  9.  
  10. public string BackgroundColor
  11. {
  12. get
  13. {
  14. return _bgcolor;
  15. }
  16. set
  17. {
  18. _bgcolor = value;
  19. }
  20. }
  21. }
  22.  
  23. public class Presenter
  24. {
  25. private IMyView _view;
  26.  
  27. public IMyView View
  28. {
  29. get
  30. {
  31. return _view;
  32. }
  33. }
  34.  
  35. public Presenter(IMyView view)
  36. {
  37. _view = view;
  38. }
  39. // business logic;
  40. }

La forma en que usamos las clases es esta:

  1. SomeView view = new SomeView();
  2. Presenter presenter = new Presenter(view);
  3. presenter.View.BackgroundColor = "black";

Si en un momento determinado vamos a usar una clase que implementa IMyView pero que ademas añade algunas propiedades o funciones que no están en la interfaz, entonces necesitamos hacer typecast:

  1. SpecialView view = (SpecialView) presenter.View;
  2. view.SpecialField = "whatever";

Puesto que presenter.View es del tipo IMyView, no podemos hilar más fino que eso, y desde que la jerarquía de clases crece empezamos a tener que hacer typecasts. La desventaja es la misma que en el caso anterior, que el error sólo aparece en tiempo de ejecución. La transformación a generics es sencilla:

  1.  
  2. public class GPresenter<T>
  3. where T: IMyView
  4. {
  5. private T _view;
  6.  
  7. public T View
  8. {
  9. get
  10. {
  11. return _view;
  12. }
  13. }
  14.  
  15. public GPresenter(T view)
  16. {
  17. _view = view;
  18. }
  19. // business logic
  20. }
  21.  
  22. SomeView smView = new SomeView();
  23. GPresenter<SomeView> gpresenter = new GPresenter<SomeView>(smView);
  24. gpresenter.View.BackgroundColor = "black";

Si en lugar de instanciar SomeView, instanciamos SpecialView, el autocompletado del IDE nos ofrecerá el campo SpecialField al teclear gpresenter.View.. Esto evita el error en tiempo de ejecución, lo cual es muy valioso. La clase GPresenter hace lo mismo que Presenter, pero su relación con las clases que implementan IMyView es fuértemente tipada, y eso le hace ganar en eficiencia y detección de errores.
Forma de detectar el uso de generics: Estamos haciendo typecasts desde interfaces a clases concretas.

  • Métodos con Generics

Esto no es más que una extensión de lo anterior. Supongamos que la clase GPresenter tiene un método que realiza determinada operación y como resultado devuelve un objeto. Podemos saber que el objeto será de tipo IMyView, y escribir la firma del método así:

  1. public IMyView SomeOperation()

Sin embargo, esto nos lleva denuevo al caso del typecast anterior. La solucion es la forma generica:

  1. public T SomeOperation<T>()

Si este tipo es distinto del que usamos para View, podemos agregar otro tipo genérico a la definición de la clase:

  1.  
  2. public class GPresenter<T, Y>
  3. where T: IMyView
  4. where Y: IMyOtherInterface
  5.  

Más que llamar a los parámetros genéricos, T e Y, conviene ponerles nombre, e.j, View y LoQueSea. La sintaxis de genérics no pone restricciones a los nombres que queramos poner a los parámetros.

  • Generics para envolturas (wrappers)

¿Cómo hacer genéricas clases cuyo código fuente no tenemos?. Como ejemplo os propongo el código fuente del GenericComboBox para WPF de DesktopRails. El control ComboBox de WPF tiene un campo Items que son los elementos que contiene el combo (dropdown o desplegable, como le querais llamar). Este campo es una lista de System.Object para que se pueda meter cualquier tipo de objeto y para que un combo pueda contener diferentes tipos de objeto. Sin embargo yo necesitaba saber que en determinadas ocasiones los elementos del combo eran todos del tipo string, o del tipo int para evitar errores en ejecución. La solución es crear una nueva clase que envuelve a la que nos interesa. Dentro de dicha clase estamos obligados a hacer typecast en algún momento, con lo que aquí no ganamos en eficiencia, pero ganamos en detección de errores en tiempo de compilación.

  1.  
  2. public class MyWrapper<T>
  3. {
  4. private TheClassWeWantToWrap _myWrap;
  5. // ...
  6. public List<T> Items
  7. {
  8. get
  9. {
  10. return (T)_myWrap.Items;
  11. }
  12. set
  13. {
  14. _myWrap.Items = value;
  15. }
  16. }
  17. }
  • Los Generics NO son polimórficos

Una cosa que hay que tener clara es que cuando se usa un tipo genérico, no se está heredando de él, simplemente se está diciendo al compilador que haga una seria de sustituciones en la plantilla. Dada la clase MyGenericBase:

  1.  
  2. public class MyGenericBase<T>
  3.  

Las instancias:

  1.  
  2. MyGenericBase<int> instanceY = ...;
  3. MyGenericBase<string> instanceX = ...;
  4.  

No son hijas de MyGenericBase, solo son sustituciones. Para que haya polimorfismo hay que extender al definiar la clase:

  1.  
  2. public class PresenterChild<T> : GPresenter<T>
  3. where T: IMyView
  4.  

Por tanto en diseños de clases, normalmente la clase genérica base suele implementar una interfaz de modo que tanto su jerarquía como las instancias que usen esa plantilla pertenezcan a la familia de la interfaz:

  1.  
  2. public class MyGenericBase<T> : IMyHierarchy
  3.  

Véase como ejemplo más complejo el AbstractController de DesktopRails.

  • La declaración de los tipos genéricos NO es recursiva pero sí compleja

Algunas definiciones de clases genéricas son difíciles de leer, como le ocurre a BusinessBase del framework CSLA:

  1.  
  2. public abstract class BusinessBase<T> : BusinessBase
  3. where T: BusinessBase<T>
  4.  

A primera vista parece una definición recursiva, imposible de interpretar. Sin embargo el compilador sólo hace la sustitución una vez y se usaría así:

  1.  
  2. public class Customer: BusinessBase<Customer>
  3.  

BusinessBase es una template genérica que opera sobre un parámetro T. Así algunos métodos devuelven como resultado un objeto de tipo T. Al imponer en la cláusula "where" que el tipo T, debe ser sí mismo, lo que hacemos es eliminar el parámetro genérico en la clase que extiende de ella. Es decir, si B hereda de A, entonces B tambien es A, de ahí el truco. Esta es una definición rebuscada. El autor símplemente quería tener una clase sin parámetros genericos para que al usarla, en lugar de hacer:

  1.  
  2. BusinessBase<Customer> customer = new BusinessBase<Customer>();

Pudiera directamente hacer:

  1. Customer customer = new Customer();

Pero reusando toda la jerarquia de BussinesBase que casualmente usa parámetros genéricos. Esto es un truco que podemos recordar cuando queramos eliminar los parámetros genéricos de una clase que hereda de otra genérica.

Véamos otra clase que escribí para DesktopRails. Se trata de una relación como en el primer ejemplo de este artículo, en este caso entre Controller y View. Lo que pasa es que la relación es doble. Necesitaba que desde Controller pudiera haber una referencia a View, pero desde View quería que también hubiese una relación a Controller, porque se hacen llamadas en ambos sentidos.

  1.  
  2. public interface IView<C> : IView
  3. where C : AbstractController
  4. ...
  5. public abstract class AbstractController<V, C> : AbstractController
  6. where C : AbstractController<V,C>
  7. where V : IView<C>
  8.  

Modo de uso:

  1.  
  2. public class UsersListController : AbstractController<IUsersListView, UsersListController>
  3. ...
  4. public interface IUsersListView : IView<UsersListController>
  5.  

En este caso, una instancia de UsersListController no conoce con precision de que tipo es su View(porque la clase tiene un campo View del tipo genérico IUsersListView), solo sabe que es de la jerarquia IUsersListView. Sin embargo, la instancia de una clase que implemente IUsersListView sí que sabe con precisión cual es el tipo exacto de Controller que tiene asociado, es una relación fuertemente tipada en esta dirección.
Lo que estas cláusulas "where" aseguran es que cuando al controlador X, se le pase una vista Y, esa vista Y debe haber sido definida como vista que puede usar un controlador X.
Así podemos descubrir errores como éste en tiempo de compilación:

  1.  
  2. public interface IUsersListView: IView<ControllerX>
  3. ...
  4. public class UsersListController : AbstractController<IUsersListView, UsersListController>
  5.  

Lo anterior no compilaría. Es útil darse cuenta de que la asociación que se está haciendo es incorrecta, en tiempo de compilación y no que falle en tiempo de ejecución.
Regla fácil:
Cuando vemos una declaración que parece recursiva, donde la cláusula "where" del parámetro genérico es igual a la propia declaración, eso es como decir, sí mismo. La clase está diciendo, yo misma. Entonces el modo de uso es repitiendo el nombre:

public class UsersListController : AbstractController<IUsersListView, UsersListController>

A la hora de escribir una declaración como AbstractController, sabremos que debemos usar esta sintaxis de aspecto recursivo cuando queremos hacer referencia a la propia clase que estamos definiendo. Esto será cuando queramos hacer una referencia entre varios tipos genéricos y a uno de ellos se le dice que su relación con otro es el otro mismo.

Otro ejemplo menos complicado contenido en DesktopRails es el AbstractData:

  1.  
  2. public abstract class AbstractData<TBaseView, C, TFinalView> : IUIData
  3.  
  4. where TBaseView : IView
  5.  
  6. where C : AbstractController
  7.  
  8. where TFinalView : TBaseView
  9.  

En esta definición, se pasa el parámetro TBaseView sólo para forzar que es padre de TFinalView en la jerarquía. En verdad no se llega a usar TBaseView sino que sólo se usa para forzar una jerarquía. El sentido a esta relación se le ve cuando vemos clases derivadas:

  1.  
  2. public abstract class AbstractUsersListData<T> : AbstractData<IUsersListView, UsersListController, T>, IUsersListData
  3. where T : IUsersListView
  4. ...
  5. public class TestUsersListData : AbstractUsersListData<TestUsersListView>
  6.  

Al ver ésta última clase de la jerarquía puedes entender que estamos forzando a que TestUsersListView sea una vista que implementa IUsersListView.

La verdad es que cuando estas diseñando las clases, inicialmente a uno no se le ocurre escribir estas cláusulas a no se que tengas muuucha experiencia escribiendo clases iguales. Lo que suelo hacer, al menos yo que no tengo más materia gris que la media, es ir modificando las cláusulas según me va haciendo falta e ir dejando que el compilador me ayude a ver si todo va bien. Suelo pensar en términos de... "si alguien usa mi clase, quiero que la use de esta manera, y quiero que si la usa de tal otra forma el compilador le de un error y no le deje". Más bién diseño al revés. Pienso en cómo quiero usar la clase y la voy diseñando. Al estilo TDD.
Existen más cláusulas "where" que se pueden especificar y que podeis leer en documentación. Por ejemplo si no sabes con exactitud la interfaz que quieres que implemente el tipo T pero sabes que quieres que sea una clase y no un tipo básico, puedes poner esto:

  1. where T : class

Agradezco que las dudas o sugerencias sobre éste artículo se gestionen en forma de comentarios en el mismo, así como los errores que podais encontrar en él. Espero que os sea de utilidad.

Antipatrones en TDD

Esta es una traducción del artículo de James Carr, TDD Anti-Patterns. En dicho artículo algunos antipatrones han sido escritos por Frank Carver, Tim Ottinger, Cory Foy, Joakim Ohlrogge y Kelly Anderson. Como en otras traducciones, la terminología de dobles de tests es difícil de traducir por lo que algunos términos los he dejado en inglés. También me he tomado la libertad de añadir alguna aclaración propia.


Reciéntemente empezé a escribir un artículo sobre Antipatrones en TDD y dedicí hacer un primer borrador con los patrones más comunes que otros y yo mismo habíamos encontrado "en estado salvaje". Entonces envié lo que había encontrado a la lista de testdrivendevelopment de yahoo y obtuve una excelente respuesta.
Como nota, ten en cuenta que esto es de momento un catálogo, méramente. Espero que se expanda ámpliamente según vamos trabajando.

Catálogo de Antipatrones en TDD

El Mentiroso
Un test completo que cumple todas las sus afirmaciones (asserts) y parece ser válido pero cuando cuando se inspecciona más de cerca muestra que realmente no esta probando su cometido en absoluto.

Setup Excesivo
Es un test que requiere un montón de trabajo para configurarlo incluso antes de que empiece. A veces se usan varios cientos de líneas de código para configurar el entorno de dicho test, con varios objetos involucrados, lo cual puede hacer difícil decir qué es lo que se está probando debido a tanto "ruido" como hay en su configuración.

El Gigante
Un test que aunque prueba correctamente el objeto en cuestión, puede contener miles de líneas y probar muchísimos casos de uso. Esto puede ser un indicador de que el sistema que estamos probando es un Objeto Dios

El Imitador
A veces usar mocks puede estar bien y ser práctico, pero otras el desarrollador se puede perder imitando los objetos que no están a prueba. En este caso un test unitario contiene tantos mocks, stubs y/o falsificaciones, que el systema a prueba ni siquiera se está probando, en su lugar estamos probando lo que los mocks están devolviendo.

El Inspector
Un test unitario que viola la encapsulación en un intento de conseguir el 100% de covertura de código y por ello sabe tanto del objeto a prueba, que cualquier intento de refactorizarlo rompería dicho test y requiere que cualquier cambio en el objeto se vea reflejado en él.

Sobras Abundantes
Es el caso en que un test unitario crea datos que se guardan en algún lugar, y otro test los reusa para sus propios fines. Si el "generador" de los datos se ejecuta despues, o nó se llega a ejecutar, el test que usa esos datos fallará por completo.

El Héroe Local
Un test que depende del entorno de desarrollo específico en que fué escrito para poder ejecutarse. El resultado es que el test pasa en dicho entorno pero falla en cualquier otro sitio. Un ejemplo típico es poner rutas que son específicas de una persona, como una referencia a un fichero en su escritorio.

El Cotilla Quisquilloso
Un test unitario que compara la salida completa de la función que se prueba, cuando en realidad sólo está interesado en pequeñas partes de ella. Esto se traduce en que el test tiene que ser continuamente mantenido a pesar de que los cambios sean insignificantes. Este es endémico de los tests de aplicaciones web. Ejemplo, comparar todo un html de salida cuando solo se necesita saber si el title es correcto.

El Cazador Secreto
Un test que a primera vista parece no estar haciendo ninguna prueba por falta de afirmaciones (asserts) pero como dicen, "el diablo está en los detalles". El test está en verdad confiando en que se lanzará una excepción en caso de que ocurra algún accidente desafortunado y que el framework de tests la capturará reportando el fracaso.

El Escaqueado
Un test unitario que hace muchas pruebas sobre efectos colaterales (presumiblemente fáciles de hacer) pero que nunca prueba el auténtico comportamiento deseado. A veces puedes encontrarlo en tests de acceso a base de datos, donde el  método a prueba se llama, despues el test selecciona datos de la base de datos y hace afirmaciones sobre el resultado. En lugar de comprobar que el método hace lo que debe, se está comprobando que dicho método no alteró ciertos datos o lo que es lo mismo, que no causó daños.

El Bocazas
Un test unitario o batería de tests que llenan la consola con mensajes de diagnóstico, de log, de depuración, y demás forraje, incluso cuando los tests pasan. A veces durante la creación de un test es necesario mostrar salida por pantalla, y lo que ocurre en este caso es que cuando se termina se deja ahí aunque ya no haga falta, en lugar de limpiarlo.

El Cazador Hambriento
Un test unitario que captura excepciones y no tiene en cuenta sus trazas, a veces reemplazándolas con un mensaje menos informativo, pero otras incluso registrando el suceso en un log y dejando el test pasar.

El Secuenciador
Un test unitario que depende de que aparezcan en el mismo orden, elementos de una lista sin ordenar.

Dependencia Oculta
Un primo hermano del Héroe Local, un test unitario que requiere que existan ciertos datos en alguna parte antes de correr.  Si los datos no se rellenaron, el test falla sin dejar apenas explicación, forzando al desarrollador a indagar por acres de código para encontrar qué datos se suponía que debían haber.

El Enumerador
Una batería de tests donde cada test es simplemente un nombre seguido de un número, ej, test1, test2, test3. Esto supone que la misión del test no queda clara y la única forma de averiguarlo es leer todo el test y rezar para que el código sea claro.

El Extraño
Un test que nisiquiera  pertenece a la clase de la cuál es parte. Está en realidad probando otro objeto (X), muy probablemente usado por el que se está probando en la clase actual (objeto Y), pero saltándose la iteración que hay entre ambos, donde el objecto X debía funcionar en base a la salida de Y, y no directamente. También conocido como La Distancia Relativa.

El Evangelista de los Sistemas Operativos
Un test unitario que confía en que un sistema operativo específico se está usando para ejecutarse. Un buen ejemplo sería un test que usa la secuencia de nueva línea de windows en la afirmación (assert), rompiéndose cuando corre bajo Linux.

El que Siempre Funciona
Un test que se escribió para pasar en lugar de para fallar  primero. Como desafortunado efecto colateral, sucede que el test siempre funciona, aunque debiese fallar.

El Libre Albedrío
En lugar de escribir un nuevo test unitario para probar una nueva funcionalidad, se añade una nueva afirmación (assert) dentro de un test existente.

El Unico
Una combinación de varios antipatrones, particularmente El Libre Albedrío y El Gigante. Es un sólo test unitario que contiene el conjunto entero de pruebas de toda la funcionalidad que tiene un objeto. Una indicación común de eso es que el test tiene el mismo nombre que su clase y contiene múltiples líneas de setup y afirmaciones.

El Macho Chillón
Un test que debido a recursos compartidos puede ver los datos resultantes de otro test y puede hacerlo fallar incluso aunque el sistema a prueba sea perfectamente válido. Esto se ha visto comúnmente en fitnesse, donde el uso de variables de clase estáticas usadas para guardar colleciones,  no eran limpiadas adecuadamente después de la ejecución, a  menudo repercutiendo de manera inesperada en otros tests. Tambíen conocido como El huésped no invitado.

El Lento Escabador
Un test unitario que se ejecuta increíblemente lento. Cuando los desarrolladores lo lanzan les da tiempo a ir al servicio, agarrar un cigarro, o peor, dejarlo corriendo y marcharse a casa al terminar el día.

Bien, eso es todo por ahora. Me gustaría que la gente pudiera votar por los diez mejores antipatrones listados aquí para que pueda escribir más descripciones, ejemplos, síntomas y (con suerte) refactorizaciones para eliminar el antipatrón de la lista.
Si he dejado alguno fuera hacédmelo saber, cualquier patrón adicional será más que bienvenido!


En los comentarios del post de James hay algunos comentarios muy interesantes que iré traduciendo más adelante en forma de actualizaciones de este post. Los nombres que los autores eligieron para algunos de los antipatrones han sido realmente dificiles de traducir y en algunos casos (pocos casos) la traducción difiere bastante de su significado en inglés.

Mocker passthrough

Another nice feature in mocker is passthrough. It is perfect when you want to ask if a given call was performed but  calling the real object rather than the mock so getting a real result. This is an example.

  1.  
  2. def testRetrieveServicesList(self):
  3. dataSource = ServersInfoService()
  4. mockSource = self.mocker.proxy(dataSource)
  5.  
  6. mockSource.srv_getServicesList()
  7. self.mocker.passthrough()
  8.  
  9. self.mocker.replay()
  10.  
  11. retValue = clientLib.retrieveServicesList(mockSource)
  12. retValue2 = clientLib.retrieveServicesList(mockSource)
  13. self.assertEqual(retValue, retValue2)
  14.  

The test above is testing the behavior of method which is using a cache. Method clientLib.retrieveServicesList is accesing a dataSource for the first time but the next time it has to get the data from the cache. So I call the method twice in replay mode but I set only one call to the dataSource in the expectations. The first call, passthrough makes sure the call is done in the real object, not the mock.
The cache test should be a separate unit test just to test the getValue/setValue but, as this is Python I try to write at least a simple test for every method, just to make sure there are no typo errors or API errors that a compiler would find out. If I was just testing the cache, I would not need the passthrough but just a mock result. However, using the real result means that retrieveServicesList is going to work with real data and that is what I was looking for now.