Año 1940, eran los inicios de la Segunda Guerra Mundial, y un joven matemático recibía en su oficina a agentes del servicio de inteligencia británico que buscaban desesperados a alguien que los ayudara a descifrar las comunicaciones interceptadas del ejército alemán.
Era Alan Turing. Es una historia conocida; puede que la hayas visto en la película The Imitation Game, del 2014.
En unos meses, junto a un equipo de matemáticos y estadistas, Turing logró descifrar el método con el que el Eje encubría sus comunicaciones. Este descubrimiento le dio a los Aliados una ventaja que se mantuvo oculta en su momento, pero probablemente redujo la duración de la guerra en varios años.
Unos años más tarde Alan Turing es condenado por la corona británica por el delito de gross indecency y elige someterse a una castración química en vez de ir a la cárcel. Muere cinco años después por comer una manzana envenenada con cianuro. Recién en el año 2013, después de más de una década de presiones públicas para hacerlo, la Reina Isabel le dio un indulto póstumo a quien hoy se reconoce como el padre de la computación teórica.
La historia de Turing como codebreaker es solo una anécdota para quienes hemos podido estudiar un poco más de su trabajo, que con una mezcla de genialidad e ingenuidad se enfrentaba a los problemas más importantes de un campo que aún no existía. Problemas que siguen siendo vigentes y para los que desarrolló teorías que se mantienen entre los pilares de la ciencia de la computación, como el teorema de computabilidad de Church-Turing, la Máquina de Turing y uno que volvió a estar de moda cuando la Inteligencia Artificial entró con todo en nuestro día a día: el Test de Turing.
Volvamos un poco a la parte de su historia que la película no alcanza a contar.
Después de su condena, Turing se aleja de la computación teórica y empieza a investigar sobre biología computacional. Solo alcanzó a publicar un paper, en el que pretende resolver un problema no menos ambicioso que su trabajo en computación: modelar matemáticamente el desarrollo del embrión. En The Chemical Basis of Morphogenesis plantea que con un conjunto de reacciones químicas y reglas simples de evolución, se pueden generar macroestructuras que forman lo que podría llegar a ser un ser vivo. Conjeturó casi accidentalmente (porque no es el foco del paper) que bajo algunas condiciones existían reacciones químicas que oscilaban entre dos compuestos distintos, y que eventualmente se validaron empíricamente con experimentos como la reacción de Belousov–Zhabotinsky.


Simulación de uno de los sistemas que propone Turing y patrones en la piel de un pez globo (fuentes: turing, pez globo)
¿Bueno, pero qué tiene que ver todo esto con los mocks en los tests? ¿Y antes que eso, qué son los mocks en los tests?
Cuando escribimos software le damos instrucciones al computador sobre cómo manipular datos e interactuar con otros sistemas. Por ejemplo, le damos las reglas de cómo instruir una orden de compra de algún activo, validando que el balance sea suficiente, que se descuente el precio de compra correcto, se mande un correo de confirmación, etc. Para validar que las reglas que programamos se interpretan correctamente para los distintos casos, se suele usar un segundo programa que solo sirve para verificar el primero. Estos son los famosos tests. Software que ejecuta otro software para validar que las reglas programadas se cumplen bajo condiciones controladas.
Es muy común que un sistema tenga que interactuar con otros para funcionar correctamente. En el ejemplo anterior, la ejecución misma de la orden puede depender de un servicio externo sobre el cual no se tiene control, y la pregunta natural es de cómo testeamos eso. La solución es suplantar a este servicio externo mientras se ejecutan los tests por uno que sí está bajo nuestro control. Tiene que funcionar lo más parecido posible al sistema real, pero tengo que poder cambiar su comportamiento para validar que mi programa funciona en todos los casos. Por ejemplo, tengo que hacer que falle para validar que mi sistema sigue funcionando cuando el proveedor se cae.
La solución a esto es usar mocks. La palabra en inglés dice bastante: una traducción literal podría ser imitar, pero se queda corta. Tiene también un componente de humor, podría ser burlarse o “mofarse”, aunque nadie usa esa palabra hoy en día. Lo que hace Stefan Kramer no es solo imitar, es también to mock. Es imitar solo una parte, exagerarla.
Los mocks son entonces estos componentes de software que imitan la interfaz de un sistema y justamente me permiten cambiar temporalmente su comportamiento para validar distintos casos. Y aquí nace una pregunta clásica en la ingeniería de software: ¿qué tengo que mockear? ¿la base de datos? ¿el sistema de archivo? ¿el envío de correos? ¿los logs? Si uso muchos mocks entonces estoy falsificando mucho y voy a dejar de confiar en mis tests. Si no uso mocks, tengo que ejecutar los sistemas reales que puede ser difícil, costoso o derechamente imposible.
¿Y qué tiene que ver esto con Turing?
No mucho en realidad, pero hay una parte en el paper de Turing que creo que ilustra muy bien el problema de los mocks y nos da algunas luces de cómo responder a la pregunta de cuándo usar mocks, y está en el primer párrafo de la introducción:
In this section a mathematical model of the growing embryo will be described. This model will be a simplification and an idealization, and consequently a falsification. It is to be hoped that the features retained for discussion are those of greatest importance in the present state of knowledge.
Además de ser una genialidad de redacción, el contenido tiene unas sutilezas que se aplican muy bien al estado de la computación moderna y a la ingeniería de software, y las vamos a revisar por parte, como si las estuviéramos diseccionando.
In this section a mathematical model of the growing embryo will be described
Lo único que pienso con esta oración es “¡y este quién se cree que es!”. Se necesita una arrogancia particular para pretender resolver tal problema, pero se lo permitiremos a Turing. Sin miedo al éxito.
This model will be a simplification and an idealization, and consequently a falsification
Alan nos dice que lo que quiere describir es un modelo. No el fenómeno real. Y claro, cuando modelo un problema tengo que dejar cosas de lado, y por lo tanto, termina siendo una falsificación. El modelo pretende representar el fenómeno, pero está condenado a ser un farsante. Igual que nuestros tests.
It is to be hoped that the features retained for discussion are those of greatest importance in the present state of knowledge.
Aquí nos da algo de esperanzas. El modelo es una falsificación, sí, pero es una buena falsificación. Hay partes de esta falsificación que funcionan igual al fenómeno que se está describiendo, y solo podemos esperar que estas partes retengan lo suficiente como para que el comportamiento del modelo sea capaz de predecir el comportamiento del fenómeno.
Igual que la imitación de una cartera de lujo que venden en la feria, que se ve igual a la original, pero el cierre es de plástico. Si quisiéramos estudiar los efectos en las personas en el metro cuando están cerca de una Louis Vuitton, nuestra falsificación de feria costera cumpliría el propósito. Que se vea igual a la original desde lejos sería lo importante (those of greatest importance in the present state of knowledge); y que el cierre se eche a perder al primer uso daría un poco lo mismo.
En los tests nos pasa lo mismo. Queremos modelar el funcionamiento del sistema en producción, pero sabemos que hay partes que están fuera de nuestro control. Por ejemplo en el caso de Fintual necesitamos tener el precio del dólar que viene de un sistema externo, pero aún así queremos tener tests del flujo de compra de dólares. ¿Qué hacemos entonces? Falsificamos el servicio que nos da los precios para nuestros tests. La parte más importante del test es que se muestre el botón de compra y que al apretarlo se valide el balance e instruya la orden, pero no que el servicio que nos da el precio funcione de una u otra manera. Falsificando este servicio retenemos las features que son de gran importancia para lo que queremos entender.
En Fintual tenemos una cultura fuerte de testeo de nuestros sistemas. Podemos dormir tranquilos sabiendo que los procesos más importantes y críticos se validan continuamente ante cualquier cambio. Si por agregar un nuevo gráfico en la app rompemos el flujo de compra de acciones, nos vamos a enterar mucho antes que pueda causar algún problema para nuestros clientes.
Así que si te dedicas a escribir software y tienes que hacer tests para tu código, acuérdate del regalo que nos dejó Turing para saber qué falsificar: lo justo para que lo que quede sea relevante en el estado actual del conocimiento.