Carlos Ble

Carlos Ble

I am a professional software developer, I solve problems.

I also teach and mentor developers to build better software.

Working as an independent professional since 2009.

Can I help you?

  • Do you need high quality tailor-made software?
  • Need training on TDD, clean code or refactoring?
  • Is it risky? do you need advise?
  • May I pair with you to write better code?

Events

Upcoming public training courses:

  1. [in English] May 5, 6 & 7
    TDD - Train the Trainers (iSQI)
    Potsdam, Berlin
  2. [en Español] 23, 24 y 25 de Abril.
    Curso de TDD con examen de certificacion iSQI
    Madrid
  3. [en Español] 29, 30 y 31 de Octubre.
    Curso de TDD con examen de certificacion iSQI
    Madrid

Conferences:

  1. I'll be at SocratesUK 2014
  2. I'll be at the London Test Gathering Workshops.

Archive for January, 2012



Un solo valor de retorno

Hay un mal que se esta extendiendo por la aplicación en las últimas semanas. Es el ResponseDTO. Es un objeto plano, tiene un campo "success" de tipo boolean y un campo "message" de tipo string. Debía llamarse Response pero colisionaba, así que se quedó con un mal nombre. Si le hubiesemos llamado ControllerResponse seguramente no se hubiese usado en las otras capas de la aplicación porque hubiese dado más pistas a quienes lo fuesen usar, de que su lugar es la capa controller. Sin embargo se ha colado en muchas capas de backend y no es una buena práctica:

Los metodos y funciones deben devolver un solo resultado y en ocasiones, ninguno (void). ResponseDTO se puede considerar "un solo resultado" cuando los campos "success" y "message" se necesitan el uno al otro, tal que no tiene sentido el objeto sin los dos campos. Es decir cuando el objeto es lo más atómico que la operación puede devolver. ResponseDTO se ideo para enviar a cliente (js) respuesta a determinados comandos que solicita al servidor pero no debería usarse en las otras capas de la aplicacion.

¿Dónde lo usamos mal? Tenemos métodos con un nombre interrogativo que denotan retorno boolean y sin embargo devuelven uno de estos ResponseDTO:

  • IsExistingUser
  • DoesPolicyExists
  • IsValidInput

Para cualquiera que lea la API, no es intuitivo. ¿Para qué sirve el "message" del objeto cuando el método "IsExistingUser" retorna? Para nada.

En la validación, puede tener sentido este objeto response dependiendo del contexto: Si el error de validación puede considerarse una circunstancia excepcional, entonces lo mejor es lanzar una excepción que alguien de nivel superior recogerá. Por tanto, sobra el objeto response ya que la excepción llevará el mensaje de validación particular. Si un problema de validación no se considera una circunstancia excepcional, entonces puede estar bien devolver el response con su success puesto  a false y su mensaje concreto.

Más sitios donde lo usamos mal: Métodos que disparan acciones hacia fuera de nuestro sistema

  •  SendEmail
  •  SaveUser

Los métodos que provocan acciones que salen de los límites de nuestro código (acceso a BD, a un servidor de correo, etc) no pueden garantizar la correcta consumación de la acción. Por tanto, jugar a devolver boolean para exito o fracaso es engañarnos.
Es preferible un método void que funciona sin hacer ruido cuando todo va bien y que lanza excepciones cuando se encuentra con una situación inesperada. Por ejemplo, el método que solicita el envio de un email, puede defenderse de excepciones que provoque la libreria de SMTP y traducirlas en excepciones de nuestro dominio, tales como EmailSendFailure (una excepción nuestra que hereda de ApplicationException).

¿Y es tan grave que usemos el ResponseDTO? Sí que lo es porque el código que consume esos métodos se convierte en spaguetti:

  1. var isNotValidData = !InputValidator.ValidateUserData(
  2. policyHolder.Person).success;
  3. if (isNotValidData)
  4. {
  5. response.message = Strings.T("Los datos son incorrectos.");
  6. return response;
  7. }

Del método "ValidateUserData" sólo nos interesa el campo "success", el mensaje lo está generando el llamador. No sólo es raro de leer sino que es un coñazo a la hora de escribir tests de interacción. Cuando queremos que en un test de colaboración, este método devuelva falso, tenemos que especificar que devuelva un objeto con un campo a falso. Si fuera boolean, no tendriamos que especificar nada porque el valor por defecto sería false. Esto va al hilo del posts de los tests de interacción limpios.

En el caso de métodos de acción como el envio de email, es incluso peor porque quien consume el método pensará que con un resultado verdadero el email ha llegado a su destino, como si eso lo pudiesemos asegurar de alguna manera.

Para saber qué debe devolver un método, este debe tener una única responsabilidad y debemos saber cual es. Y la mejor manera de saberlo, para mi siempre es escribir el test antes que el código.

 

Cleaner interaction tests

Note: this post will probably evolve, the text will be updated as I need it.

In order to write cleaner interaction tests (those which use test doubles; mocks, spies, stubs) you should understand how your test doubles framework works. Interaction tests can be extremely difficult to read and maintain and very fragile if you don't pay enough attention. We will review Mockito, Moq, Jasmine and pyDoubles.

Mockito and Moq create a double object by inheriting the original and overriding its methods. pyDoubles uses interception rather than inheritance. Jasmine is different because it does not double the whole object, but just the method you spy on. So there is a big difference: Mockito, Moq and pyDoubles replace the whole object while Jasmine replaces just one method.

So Jasmine leaves space for a bad testing practice: Spy on one method of the object while testing other method of the same object. Interaction tests are intentded for object collaboration. Testing that one method calls another method within the same object is not recommended. It means that the object has more than one responsibility or that you want to know more about the object's inner behavior than you should, so you are breaking encapsulation. This is not a Jasmine problem, the problem is yours if you don't use the tool properly.

There are three primary reasons to replace method calls on collaborators:

  1. You want to avoid a database call just to keep the test unit fast and repeatable (stub the method - use a stub object)
  2. You want the method to return some value to simulate the response (stub the method - use a stub object)
  3. You want to make sure that a call is made (spy or expect the call - use a spy or a mock object)

For case numer 1, you don't have to specify the returned value. Just use the stub object and let the framework return whatever it wants. Mockito and Moq, return the default value for the method, when no other behavior is specified:

  • If the method returns an integer, a call to that method in the stub object, will return cero (default value for integers)
  • If the method returns an object (reference type), a call to the stub method will return null (default for objects)
  • False for booleans and so on...

However, be careful with Moq because in C#, if the original method is not virtual (contains the virtual keyword), Moq can't stub it out, so the original will be called. Moq fails if you specify a behavior on that method but if you don't, it calls the original instead of returning the default value.

pyDoubles always returns None no matter the method signature.

For case numer 2, you define what the returned value should be. Using "when" in Mockito and "Setup" in Moq.

For case numer 3, you verify that a call was made:

  • Mockito: verify(double).method();
  • Moq: double.Verify(x=>x.method());
  • pyDoubles: assert_that_method(double.method).was_called()
  • Jasmine: expect(double.method).toHaveBeenCalled()

You might want to know what parameters were passed in too. In this case you can use Matchers in Mockito, Moq and pyDoubles.

Moq sample:

  1. userRepository.Verify(x => x.SaveNewUser(
  2. It.Is<Person>(
  3. p => p.PersonalIdentificationCode == "TEST_CODE")));
  4.  

pyDoubles sample:

  1. assert_that_method(double.method
  2. ).was_called().with_args(obj_with_fields({
  3. 'id': 20,
  4. 'test_field': 'OK'}))

For Mockito samples I am waiting for my friend @regiluze to write a post on it :-) I will link it here when written.

Jasmine sample:

  1. spyOn(double, "method").andCallFake(function(){
  2. expect(arguments[0].name).toEqual("Carlos");
  3. });

Test collaborations one to one:

If the object under test has more than one collaborator, tests can be really hard to understand. I recommend expressing every collaboration in a single test:

  • A talks to B (test 1)
  • A talks to C (test 2)
  • For test1, B will be a spy o mock (you use Verify at the end). Also for test1, you don't care about C, C is just a dummy. Calls to C don't need to be configured in the double, let the framework return the default value.
  • For test2, C is the spy or the mock, you verify interaction between A and C, and do't care about B. Calls to B don't need to be configured, let the framework return whatever.
  • For test1, if the value returned by C, is necessary for the collaboration between A and B, then Ok, specify the stub result for that call to C, and verify on B.

Group the tests by setup:

Should I have a test class for every class in the production code? NO.  Tests must be grouped by their setup. This is, put those which have the same arrangement in the same test case (test class). If there are two or more interaction tests in a class, the creation of the SUT and the doubles, should not be in every test. It must be in the setup, along with the injection (doubles, are injected to the SUT as collaborator). So, if you find yourself injecting the test double to the SUT in more than one test, watch them again carefully.

 

 

Apego a la herramienta

Cuandó programaba en Delphi arrastrando componentes gráficos para programar las aplicaciones de escritorio, me parecía tan potente y tan sencillo que no quería cambiar de herramienta (allá por 2001). Por motivos de trabajo tuve que saltar a Gnome, con Gtk (en un principio con C++ y Gtkmm) Me parecía un atraso volver a aprender nuevas herramentas para hacer cosas parecidas y se mi hizo dificil. Sin embargo, el cambio me trajo nuevos conocimientos, nuevas ideas para abrir la mente. En medio de todo estó también programé aplicaciones Java + Swing, tanto cuando no había editores de GUI visuales como cuando los hubo. Pasé por Mono y Gtk#, luego a la web con Castle.MonoRail y eso me llevó con el tiempo a MS .Net y Windows.Forms.  Siempre por motivos de trabajo, un poco en contra de lo que me apetecía. Contínuamente a aprender y encima saltando a la plataforma propietaria de Microsoft, el "maligno" (yo que ?olo tenía Linux en mi maquina desde hacía años).
Gracias a las herramientas de Microsoft pude aprender conceptos como el Data Binding que hoy en día aplico en Javascript y llegué a aprender y dominar los genéricos, que le dan mil vueltas a los de Java. Incluso me hice un framework para escritorio que me permitiese testear automaticamente mis aplicaciones.
Después por motivos de trabajo, tuve que abandonar .Net y salté a Python + Django, para trabajar ya en la web. Nada que ver con lo anterior. Nada en absoluto. En todos estos años también he tenido que trabajar con Applets Java, con PlayFramework, con Weblogic, con Spring y en los últimos meses, muchísimo Javascript.

Cada cambio era una patada a mi ego, porque me sacaba de la zona de confort y me hacía sentir que perdía mi valía, la maestría de la herramienta que tanto me habia trabajado.

Y resulta que en lugar de perder, estaba ganando. Si no hubiese tenido que pasar obligado por todos estos cambios, no tendría la visión que tengo hoy en día sobre patrones, técnicas de programación, arquitecturas ... la tranquilidad de que me cuesta muy poco adaptarme a una nueva plataforma porque siempre contiene elementos de otras que ya conozco. Me da seguridad en mi mismo y me ha quitado por completo el apego a las herramientas. Programo en lo que sea y estoy deseando seguir aprendiendo lenguajes, frameworks.

Disfruto programando cualquier lenguaje y sé qué elementos me interesan usar de un framework y qué otras partes me puedo saltar. Solo porque he tenido que trabajar en muchos.

Ahora que estamos buscando gente para que se una a nuestro equipo, donde usamos mayormente .Net con C# y por otro lado Javascript, sé que mucha gente no tiene interes porque no quiere abandonar su stack tecnológico de siempre. Parece que programar .Net fuese lo peor del mundo, cuando es tan solo una copia de Java que lo mejora en muchos aspectos. Se van copiando el uno del otro. Además puedes usar F#, IronPython, IronRuby, Boo, y aprender de algunos de los programadores más increibles del planeta.

Me parece muy bien que la gente tenga predilección por frameworks como, Django, Symfony, Rails o Grails pero la negativa a usar otra cosa, tal como yo lo veo, es una cuestión de apego  poco racional. En algunos casos, yo diría que inmadurez. Además, los proyectos no duran para siempre, son etapas, son transiciones como la vida misma.

Todos los que me habeis dicho personalmente (amigos mios) que nos veníais por el stack tecnológico, creo que aún teneis que seguir madurando profesionalmente.  Y yo tambien, por supuesto. Lo digo sin rencor ni enfado de ningun tipo.

Contínuamente me digo a mí mismo... no dejes que el ego frene tu aprendizaje :-)

Mockito Vs Moq

Don't be confused, the title is just to get your attention. This is not a benchmark between Mockito and Moq. I need to  explain some differences to my teammates so that the transition between Mockito and Moq is softer for them.

Typical way to stub out a method call with mockito:

when(colaborator.method(argument1)).thenReturn(whateverIwant);

Same with Moq:

colaborator.Setup(x => x.method(argument1)).Returns(whateverIwant);

Obviously, the mockito way is nicer. I believe this is because of Java capabilities but I am not sure. I would like to dig into mockito's source code to see how is this possible. I will do it and blog about it soon. I used to think that it was because of the kind ot late binding supported by Java, but I am not sure.

Moq uses a lambda expression (anonymous method inlined) to tell the framework which method and how is expected to be called). The reason I use "x" for the object in the anonymous method is because it refers to "colaborator" itself so there is no point in adding more names.

The other important difference relates to the "virtual" keyword in C#. Methods in C# are final by default. You can't override them unless they contain the keyword "virtual" on its signature. Both, Mockito and Moq create test doubles by subclassing on runtime, overriding methods and implementing the fake behavior on them (as far as I know). So, if the method you want to stub is not virtual, Moq will throw a runtime exception. If it is not virtual but you don't specify any fake behavior on it, it will not throw exceptions, but worse, it will call the actual implementation. So you might be hiting the database in your unit tests inadvertedly. What I do, is that all my methods are virtual by default (I write "virtual" on their signature).

There will be more posts on this topic :-)

RS: Aprovechar la experiencia

En el sprint de esta semana nos hemos dado cuenta de errores cometidos semanas atrás. Las acciones más importantes ahora para seguir mejorando, son:

  • Que cuando reimplementemos una funcionalidad (o una similar a otra que ya tuviesemos), contemos con las personas que desarrollaron la primera versión. Lo ideal es que las personas que estuvieron en el primer desarrollo estén haciendo pareja con quienes se enfrentan al problema por primera vez. Si no es posible, al menos deben sentarse a hablar de las dificultades que surgieron con la primera implementación y las soluciones que se encontraron.
    Lo primero será tratar de evaluar si se puede reutilizar parte del código existente. No siempre es fácil tomar esta decisión pero por lo menos hacer un estudio rápido de la situación. En caso que se pueda reutilizar, estudiar qué se debe refactorizar y a qué se le pueden añadir tests. En caso que haya que reescribirlo todo por completo, tener presentes las ideas que ayudaron más al buen funcionamiento de la primera implementación. Esto es: aprovechar la experiencia que ya tenemos.
  • Separar muy bien lo que es importante de lo que es urgente. Si todo es urgente, dejamos de saber lo que es importante. Nos ha pasado y nos ha perjudicado. Cuando digamos que algo es urgente, tendremos que saber que corremos peligro de dejar atrás lo imporante.
  • Tratar de evitar la entrada de imprevistos durante el sprint. Tiene que ver con lo anterior. Si surge una necesidad el jueves y no es bloqueante, debemos plantearnos esperar a abordarla el lunes, es decir, en el siguiente sprint. La idea es no romper la planificación semanal. Cuando la rompemos, nos distanciamos de los objetivos propuestos y entonces dejamos de ser predecibles. Ser predecibles es una de nuestras metas porque así sabremos cada vez mejor, cuánto podemos tardar en entregar funcionalidad con valor para el negocio.
  • Evitar hablar de "terminar tareas" y hablar más de poner en producción tareas que suman valor. No sirve querer terminar, sino querer desarrollar piezas que mejoran la experiencia del usuario. Cuando pensamos en terminar, es más fácil hacerlo mal (porque lo urgente quita de enmedio a lo importante). Rara es la semana que no se introducen nuevos requisitos en funcionalidad que estaba "terminada".

Esta semana estoy muy contento con la labor técnica del equipo:

  • Se ha respetado en todo momento que todos los tests pasen
  • Se han añadido tests para demostrar bugs
  • No recuerdo que se hayan escrito métodos nuevos de más de 10 líneas.
  • No se han introducido números mágicos ni código duplicado
  • Se ha prestado bastante atención a la code review y los commits han sido muy claros.

Es importante vigilar bien que sigamos por el buen camino, porque estamos en un punto en que retroceder lo andado no es muy dificil.

Buen comienzo de semana a todos :-)

RS: planificación de sprint

Nuestros sprints son de una semana, empiezan el lunes y terminan el viernes. Planificamos en el daily standup meeting del lunes por la mañana. Pero el daily no queremos que pase de los 20 minutos (mejor seria no pasar de los 15). Asi que las últimas dos semanas, terminabamos el meeting sin haber cerrado la planificacion semanal, sin tener las tareas estimadas y por tanto, sin conocer cuál era el objetivo a cumplir. Eso hace que al terminar la semana no sepamos si lo hemos hecho mejor o peor. Dejamos de ser predecibles cuando pasa esto.

Por este motivo, la reunion del lunes podrá estirarse hasta un máximo de una hora con tal de que todos podamos estimar y comprometernos a entregar determinada funcionalidad el viernes, o por lo menos a intentarlo.

Aunque nuestra hora de meeting son las 9.30am, el lunes desplazaremos la hora lo que haga falta para que estemos todos. Esta semana faltaron personas que al no estar en el meeting de planificación no pudieron aportar su conocimiento. Eso llevó a que yo escogiera como prioritaria una tarea que en realidad no lo era, porque ya teniamos una funcionalidad parecida, hecha en código existente pero no lo sabíamos. Era una funcionalidad que conocía la persona no estaba. La tarea que se ha implementado hacía falta pero no era tan prioritaria en el sprint de la semana que ha terminado.

Por otro lado existe preocupación porque no estamos colocando las clases en los namespaces más adecuados, ni los ficheros en las carpetas adecuadas. El problema es que si no están en un lugar intuitivo podemos repetir funcionalidad. Para esto la code review juega un papel importante, por lo que hay que seguir haciendo incapié en ella. A parte de eso, la persona que detecte que se ha puesto un fichero en el sitio incorrecto o un namespace que no corresponde, debe actuar de manera concreta, bien modificando el código o bien avisando por email a los compañeros, diciendo exáctamente por qué no es correcto y cómo solucionarlo.

En el código legado sigue habiendo horrores que nos están haciendo daño pero la preocupación por la ubicación de clases y ficheros es un tema recurrente así que vamos a intentarla solventar, tomando acciones concretas y constructivas.

Esta semana hemos resuelto graves problemas de concurrencia que teníamos. El trabajo ha sido duro porque hemos invertido mucho tiempo y escrito pocas líneas de código y eso da mala sensación, pero el resultado es muy bueno. Hemos aprendido bastante y sabemos qué cuestiones debemos considerar desde ya, para que nuestro código esté preparado para la concurrencia. Escribiré un post sobre nuestros descubrimientos con Linq2Sql y concurrencia pronto.

En las últimas semanas hemos hecho muy poco pair programming porque buena parte del equipo pensaba que así avanzaríamos más teniendo la fecha de entrega muy cercana. La realidad es que no podemos cuantificar si ha salido adelante más trabajo o no, pero lo que sí hemos hecho es introducir deuda técnica y aumentar en desconocimiento de lo que los demás hacen, lo que lleva a la duplicidad del código. Falta comunicación. Sabemos que la próxima semana volvemos a nuestro ritmo habitual, intentando hacer las cosas lo mejor posible y sabiendo que cuando dos personas trabajan juntas y bien concentradas, nos acercamos más a ello.

 

El equipo crece

Buscábamos a un artesano (o lo que a mi me vale por artesano) y hemos tenido la suerte de encontrarle. En enero se incorpora al equipo Ismael Ferrer (@ifolmedo). Tuvimos dos excelentes candidatos, la verdad. Afortunadamente la situación actual de Ismael se alinéa perfectamente con la nuestra. Cada entrevista duró un poco más de una hora. Hubo dos bloques de preguntas teóricas y una sesión de media hora de pair programming. Aquí pongo las preguntas que hicimos. Los dos entrevistados las respondieron todas estupendamente.

Bloque teórico-técnico:

  • ¿En qué consiste el patrón Singleton? ¿Cuándo lo usarías?
  • ¿En qué consiste el patrón Factory? ¿Cuándo lo usarías?
  • ¿Qué es la inyección de dependencias?
  • ¿Qué diferencia hay entre mock y stub?
  • ¿Cómo es el algoritmo TDD?
  • ¿Qué significa SOLID?
  • ¿Por qué motivo harías una clase abstracta?
  • ¿Qué libros técnicos has leido y recomendarías leer?

Bloque sobre habilidades personales (si hizo delante de nuestro tablón kanban):

  • ¿Cómo crees que funciona nuestro panel?
  • ¿Qué información puedes obtener mirandolo?
  • En medio del desarrollo de una tarea que has iniciado, se acerca el dueño de producto y te pide que añadas nuevos requisitos ¿Qué haces?
  • ¿Qué actitud de un compañero te puede llegar a irritar?
  • ¿Qué haces si detectas un conflicto abierto entre dos compañeros tuyos?
  • Un compañero se salta deliberadamente las convenciones y buenas practicas adoptadas en el equipo ¿qué harias?

Muchas gracias a todas las personas que escribieron mostrando interes por venir a hacer la entrevista y sobre todo a las dos personas que al final vinieron.

Ahora continuamos rumbo a la excelencia técnica y de producto. El lanzamiento de producto que hemos hecho esta semana, es el primer salto hacia una nueva etapa de la empresa y del equipo. A por ello!