Una sinfonía en C#

Un pequeño aporte a la comunidad de habla hispana.

IoT: ¿Cómo enviar mensajes MQTT a Azure IoT Hub?

No voy a descubrir nada diciendo que Arduino es la plataforma más popular para desarrollo de electrónica para el usuario “común”, por facilidad, por precio, por disponibilidad de información, herramientas, etc.

Si hablamos de contar con capacidad de conectarnos a una red inalámbrica con Arduino nos viene a la mente el ESP8266, este pequeño chip de bajo costo que soporta 802.11 b/g/n y cuenta con un stack TCP completo, pudiendo incluso trabajar por su cuenta gracias a que también tiene un microcontrolador incorporado. Un proyecto que nació poco después basado en el mismo chip es NodeMCU que es una plataforma para IoT que utilizar el ESP8266 y utiliza LUA como lenguaje (en realidad NodeMCU es el firmware no el hardware, pero lo vamos a usar de modo indiferente) en este caso gracias al trabajo de la comunidad existe un plugin para poder programar en C desde el IDE de Arduino que es justamente lo que vamos a hacer. Otra ventaja del hardware es que se puede conseguir por muy poco dinero y tenemos todo, wifi, entras / salidas digitales, analógicas, UART, etc.

Tener soporte MQTT en NodeMCU

La forma más común de contar con soporte para MQTT con mensajes bidireccionales y demás es utilizar la biblioteca PubSubClient y ya, funciona muy bien y no hay mucho que agregar así que eso es lo que vamos a hacer.

Acá podemos ver un ejemplo simple que nos proporciona la gente del equipo de desarrollo.

Enviando mensajes a Azure

En Azure tenemos dos servicios para recibir eventos en cantidad, Event Hubs y IoT Hub, bien, cuál usar es simple de decidir más allá de ponernos con comparativas de rendimiento y escalabilidad ocurre que Event Hubs hasta este día no soporta MQTT, así que vamos a usar IoT Hub.

Configurar IoT Hub y el cliente

Vamos a utilizar el código de ejemplo del PubSubClient y configurarlo para poder enviar mensajes, también ser podrán recibir, esta parte es un poco “mágica” porque no hay mucha información sobre los parámetros de la conexión, vamos a ver el código de ejemplo paso a paso donde tenemos que reemplazar valores y cosas.

const char* ssid = "........";
const char* password = "........";
const char* mqtt_server = "broker.mqtt-dashboard.com";

en ssid ponemos el nombre de nuestra red, en password la contraseña, esto es simple, ahora empiezan los datos raros.

en mqtt_server tenemos que poner el nombre de nuestro IoTHub, este nombre es <nombre de nuestro iothub>.azure-devices.net, en mi caso es “testesp.azure-devices.net”, quedaría así:

const char* ssid = "MI_RED";
const char* password = "PASSWORD_DE_MI_RED";
const char* mqtt_server = "testesp.azure-devices.net";

Agregar el cliente seguro de WIFI

Ya que IoTHub utiliza TSL tenemos que utilizar la versión segura de la biblioteca de conexión con la Wifi, entonces hacemos dos cambios, agregamos la referencia

#include <wificlientsecure.h>

y reemplazar la declaración de la clase WiFiClient por WiFiClientSecure así:

WiFiClientSecure espClient;

PubSubClient client(espClient);

Cambiar el puerto de conexión de MQTT

Por defecto se utilizar el puerto 1883 pero Azure utiliza 8883, quedaría así:

client.setServer(mqtt_server, 8883);

Configurando los datos de conexión

Esta es la parte más tricky, vamos a localizar la línea donde se hace la conexión

if (client.connect(clientId.c_str()))

Primero que nada vamos a utilizar otra sobrecarga, para utilizar la que recibe: deviceID, Usuario y password estos tres datos los obtenemos del siguiente modo:

El deviceID lo creamos en el IoTHub

image

En mi caso TestDevice

El usuario es simple, es el mismo nombre del nuestro IoTHub que usamos al principio con el nombre del device después, en mi caso es así:

testesp.azure-devices.net/TestDevice

Ahora viene la parte rara, en donde iría la password tenemos que poner un connection string SAS que hay que generar, la forma más simple de hacerlo es la siguiente:

Descargar el IoT hub device explorer de acá, una vez instalado volvemos al portal de Azure y buscamos la parte de Shared Access Policies, creamos una policy nueva o usamos una existente (depende de qué queremos) pero en todo caso, una vez que tenemos una vamos al detalle y copiamos el connection string de primary ( o el otro, es igual) y eso lo pegamos en el device explorer y presionamos “Generate SAS

image

Y ese SAS que se genera es justamente el password que tenemos que usar, tal cual está, entonces quedaría todo así:

if (client.connect("TestDevice", "testesp.azure-devices.net/TestDevice","SharedAccessSignature sr=testesp.azure-devices.net&sig=KKKKKKKKKKKKKKK%3D&se=1548536586&skn=blabla"))

Con esto podemos conectarnos al IoTHub, para enviar mensajes se hace publish, pero a un topic específico, en el caso de nuestro dispositivo el nombre es devices/<nombre del device>/messages/events/ tenemos que reemplazar la línea que dice:

client.publish("outTopic", "hello world");

por

client.publish("devices/TestDevice/messages/events/", "hello world");

Y para poder recibir es algo similar, cambiamos donde dice

client.subscribe("inTopic")

por

client.subscribe("devices/TestDevice/messages/devicebound/#")

Donde TestDevice es el nombre de nuestro dispositivo por supuesto

Por último, cambiar el tamaño máximo de paquetes del MQTT de la biblioteca PubSubClient:

Ya que el connection string es tan largo el máximo por defecto no nos será suficiente, tenemos que ir a buscar donde tenemos instalada la biblioteca PubSubCliente y cambiarla así:

MQTT_MAX_PACKET_SIZE 256

Y con eso ya está, ahora podemos compilar en el IDE, cargar el programa en el dispositivo, abrir el monitor serie y si todo a bien deberíamos ver que envía mensajes

image

Excelente, con esto será suficiente para enviar y recibir desde NodeMCU a Azure IoT Hub. Dejo el ejemplo de código modificado acá

Nos leemos.

Javascript tips: Jugando con strings, interpolación y otras yerbas.

Hace un tiempo (3 años) hablé sobre cómo tener strings multilínea en Javascript, una opción no tan conocida incluso hoy en día.

Bien, con las posteriores versiones del estándar y los navegadores de hoy en día ya contamos con algunas características más avanzadas para el manejo de strings, vamos a ver un poco de qué se trata.

Interpolación de strings

Una de las novedades de ECMA Script 6 2015 son los template string (o string interpolado) muy similar a C#, nos permite tener un texto (template) y que el interprete reemplace ciertas cadenas especiales con variables que están dentro del contexto, por ejemplo

var s = "mundo";
`Hola ${s}`

En este caso la variable s tiene el valor “mundo” y cuando hacemos referencia en la siguiente línea a la variable por nombre escribiéndola con esa sintaxis ${variable} y usando las comillas invertidas igual que el en caso de los strings multilínea.

Por supuesto que podemos hacer referencia a objetos complejos

var o = {"nombre": "Leonado", "twitter": "@leomicheloni"}
`Hola, mi nombre es ${o.nombre} puedes seguirme en twitter en ${o.twitter}`
"Hola, mi nombre es Leonado puedes seguirme en twitter en @leomicheloni"

Perfecto, y muy útil para que nuestro código sea más limpio y sea menos propenso a errores, pero hay más.

Qué pasa si hacemos referencia a algo que no existe, por ejemplo a una propiedad que no existe en el objeto, nada, como Javascript es dinámico simplemente nos muestra undefined.

var o = {"nombre": "Leonado"}
`Hola, mi nombre es ${o.nombre} y mi apellido es ${o.apellido}`
"Hola, mi nombre es Leonado y mi apellido es undefined"

Perfecto, no problem.

Sin embargo si hacemos referencia a una variable que no existe….

`Hola, mi nombre es ${nombre}`
Uncaught ReferenceError: nombre is not defined
    at :1:23

Pumm! se rompe, es decir se comporta igual que si concatenando el strings a la viaja usanza.

"Hola, mi nombre es " + nombre

Pero hay más

Procesar expresiones

También es posible procesar expresiones dentro de un template string, por ejemplo

`Hola, la suman de 1+2 es ${1+2}`
"Hola, la suman de 1+2 es 3"

Por supuesto que podemos sumar elementos de objetos

var o = {a : 1, b: 7};
`La suman de ${o.a} + ${o.b} es ${o.a + o.b}`
"La suman de 1 + 7 es 8"

Y listo, no podemos reemplazar una biblioteca de procesamiento de templates como Mustache con esto pero nos permite ahorrar trabajo y evitar errores, además de hacer nuestro código más limpio.

Nos leemos.

Primeros pasos con Smart Contracts

¿Qué son los Smart Contracts?

En pocas palabras un Smart Contract es código ejecutable que corre sobre una Blockchain, básicamente escribimos código y lo desplegamos en una transacción y el código queda dentro de la Blockchain, para siempre.

Una forma de verlo es como un objeto que tiene un estado entonces una vez que el Smart Contract está en la Blockchain ese estado (entendiendo como estado a el conjunto de valores de los atributos de un objeto en un momento dado) no se puede alterar porque al estar grabado en una transacción en la Blockchain es inalterable.

La forma de alterarlo es generando una nueva transacción (invocando algunas de las operaciones del Smart Contract) de este modo si lo que hacemos cambia el estado del Smart Contract se generará una nueva transacción con el nuevo estado, otra vez, para siempre.

Al igual que una Blockchain que usamos para guardar datos y a medida que modificamos, por ejemplo, el estado de nuestra cuenta se genera una nueva transacción con el nuevo estado (al estilo libro de balances) lo único que podemos hace es conocer el historial de lo que fue pasando, cómo se modificó a través del tiempo pero no modificar lo existente sino que solo podemos generar un nuevo estado.

Lo mismo pasa con los Smart Contracts, si tengo un objeto que por ejemplo almacena un contador, cada vez que invoque la operación “incrementarContador” del Smart Contract lo que va a ocurrir es que se generará una nueva transacción (que tendrá un costo en GAS) con el nuevo estado, pero nunca podremos alterar el pasado.

Esto puede ser confuso, pero vamos a intentar verlo con ejemplos en breve.

¿Cómo hago para crear y probar un Smart Contract?

He hecho algunos videos al respecto, pero lo cuento brevemente acá otra vez.

Test RPC

Test RPC es una Blockchain en memoria, compatible con Ethereum, es decir, soporta Smart Contracts (el estándar de facto al menos), y como es en memoria es inmediato, además de que no consumimos GAS real ni hay que esperar a que las transacciones se confirmen, entonces es una gran forma de aprender, en general nuestro flujo de trabajo será:

  • Desarrollar y probar con Test RPC
  • Probar sobre TestNet (que es una red Ethereum real pero para hacer pruebas)
  • Desplegar en la red productiva

Hay otras cosas que vamos a poder hacer gracias a Test RPC como por ejemplo unit testing a nuestros Smart Contracts, pero eso lo dejamos para más adelante, comencemos por lo básico

¿Cómo programo un Smart Contract?

La forma más común de hacerlo es programando en un lenguaje de alto nivel llamado Solidity (si bien hay alternativas), compilar y desplegar los bytecodes en la Blockchain, eso es lo que vamos a hacer ahora mismo paso a paso.

Solidity

Solidity es similiar a Javascript en su sintaxis pero es tipado, así que nos va a resultar bastante familiar su uso, hay un par de formas de poder compilar y desplegar un contrato

  • Utilizar un compilador online
  • Utilizar VSCode y un plugin
  • Utilizar cualquier editor y Truffle
  • Otros

Nostros vamos a usar VSCode y Truffle que es bastante cómodo y nos va a servir además de para compilar, para hacer Unit test e incluso desplegar.

Truffle

Truffle es un framework, es decir, un conjunto de herramientas, tiene varias cosas

  • Compilador Solidity
  • Posibilidad de ejecutar Unit test tipo Jassmine
  • Capacidad de desplegar a cualquier cliente RPC, como Test RPC o una Blockchain como Ethereum

Entonces, instalando Test RPC y Truffle tenemos todo lo que necesitamos para desarrollar nuestros Smart Contracts sin más.

Como casi todo en este mundo de hoy en día hace falta tener NodeJs para instalar tanto Test RPC como Truffle, además de otras cosas.

Ahora si, comenzamos a escribir un contrato

Una vez terminada la sección “instalar cosas varias horas” estamos listos para escribir el primer contrato, la idea es hacer todo el flujo, para que se puedan relacionar las ideas y queden claros los conceptos.

Los primero que vamos a hacer es usar Truffle para crear un proyecto de ejemplo, eso nos va a ayudar con varias de las tareas de creación de scripts de deploy y testing, etc., vamos al directorio donde queremos crear nuestro proyecto y hacemos

truffle init

después de un momento abrimos con VSCode haciendo

code .

después iniciamos Test RPC y listo, vemos el proyecto creado por Truffle con varios archivos, vamos a ver un poco de qué se trata

Untitled

Apenas ejecutamos Test RPC nos da alguna información, vamos a las cuentas (remarcadas en amarillo) y el último mensaje que indica en qué puerto está escuchando, que es el puerto por defecto al que Truffle envía mensajes, de momento nada que hacer para que funcione. Veamos qué generó Truffle por nosotros.

 

vscode

Ok, como dije Truffle genera todo un “entorno” de trabajo, es decir, además de un contrato de ejemplo (que probaremos en breve) genera algunas otra cosas, como por ejemplo Unit Tests y mecanismos para publicar el contrato en una Blockchain, en nuestro caso Test RPC.

En este caso vamos a centrarnos en la carpeta “contracts” allí pondremos se encuentran los contratos creados por Truffle a modo de demo y pondremos los nuestros en el futuro, vamos a abrir Metacoin.sol que es el que nos interesa.

pragma solidity ^0.4.4;

contract MetaCoin {
	mapping (address => uint) balances;

	// Constructor del contrato, se ejecuta solo cuando el contrato es desplegado
	// en la Blockchain por primera vez
	function MetaCoin() {
		balances[tx.origin] = 10000;
	}

	// Ejecuta una transferencia, modifica los valores del mapping del contrato
	// por lo tanto modifica su estado, requiere una transacción y consumen GAS
	function sendCoin(address receiver, uint amount) returns(bool sufficient) {
		if (balances[msg.sender] < amount) return false;
		balances[msg.sender] -= amount;
		balances[receiver] += amount;
		return true;
	}

	// Retorno el valor del mapping en una posición particular
	// no ejecuta ninguna transacción, es inmediato
	function getBalance(address addr) returns(uint) {
		return balances[addr];
	}
}

He simplifica un poco el ejemplo que viene en el proyecto que genera Truffle pero es esencialmente lo mismo, también agregué algunos comentarios en el código, vamos a ver un poco de qué se trata.

En principio se trata de una meta-moneda, es decir, es decir, tendremos una cantidad de saldo en el contrato que se irá transfiriendo entre los participantes.

Primero, este contrato es en sí un objeto y se declara con la palabra clave contract.

Tiene tres funciones, por defecto todas públicas:

  • Metacoin: Es el constructor, y solo se ejecutará cuando se despliegue el contrato en la Blockchain, además vemos que se inicializa el mapping (un diccionario) con un valor de 10000 en el address de quien crea el contrato, esto es para que quien despliegue tenga esa balance la primera vez.
  • sendCoing: Realiza una transferencia entre cuentas, simplemente verifica que el balance de quien quiere enviar fondos sea suficiente y luego pasa el valor de un lado al otro. Notemos que en ambos casos se lee el address de quien invoca la función desde una variable de contexto llamada msg, que representa el mensaje hacia la Blockchain, dentro de él hay mucha información entre ésta el address de quien genera el mensaje. Notemos que modifica el diccionario balances por lo tanto altera el estado del contrato y por esto genera una nueva transacción en la Blockchain, esta operación no es inmediata.
  • getBalance: Simplemente retorna el valor en una dirección dada del mapping, notemos que no es más que una consulta, no modifica el estado del contracto, es decir, no genera una nueva transacción.

Compilar y desplegar nuestro contracto en Test RPC

Gracias a Truffle compilar y desplegar el contrato es bien simple (también ejecutar los Unit test, pero eso lo veremos después) no tenemos más que abrir el terminal integrado de VSCode y llamar a la consola de Truffle con el siguiente comando:

truffle console

y desde ahí podemos hacer varias cosas, empecemos compilando el contrato, para ello ejecutamos el siguiente comando

compile

de momento no demos importancia a los mensajes, vale decir que ha compilado tres cosas, nuestro contrato, una librería de utilidades y un contrato especial que genera Truffle para desplegar nuestro contrato (sí, raro, pero es así)

Bien, ahora tenemos el contrato generado, y además ha aparecido una carpeta build/contracts en el directorio del proyecto, ahí se encuentra lo que se va a desplegar en la Blockchain, vemos que son tres archivos json, uno por cada archivo Solidity que había en el directorio contracts

compile

Estos json contienen el abi del contrato (que es la metadata que permite saber cómo interactuar con el contrato) y el binario, vamos a desplegarlo en la Blockchain (TestRPC) y probar un par de cosas

Desplegando el contrato

Para desplegarlo no tenemos más que escribir en la consola

migrate

y listo, vemos un mensaje bastante largo de respuesta, entre los datos más importantes está el address del contrato (ya que ahora está en la Blockchain tiene una dirección) pero no nos preocupemos por copiar estos datos, Truffle ha actualizado los json que están en la carpeta buid/contracts con esta información.

Vamos a probar el contrato, así que vamos a usar varios comandos de Truffle para esto

Probando el contrato

Lo primero que vamos a hacer es generar un proxy al contrato, esto es, un objeto en javascript de nuestro lado (de lado cliente, quiero decir) que nos permita interactuar con el contrato sin necesidad de enviar mensajes RPC a mano (cosa que podríamos hacer pero esto es más simple) para esto lo que vamos a hacer es escribir lo siguiente

var coin;
MetaCoin.deployed().then(function(item) { coin = item;})

Básicamente Truffle nos permite acceder de manera sencilla al contracto desplegado con Metacoin.deployed, esto retorna una promesa (porque no es instantáneo) y ponemos la referencia en la variable coin.

Y si ahora hacemos

coin

Vemos un montón de información que no es otra cosa que el proxy al contrato con metadata y otras cosas, desde ahora vamos a interactuar con el contrato a través de esta variable coin.

Primero que nada, el constructor del contrato nos debería haber asignado 10000 a nuestro address (al elemento del diccionario con nuestro address como clave) ya que eso ocurre al ejecutar el constructor, pero nosotros no sabemos nuestro address, al principio vimos que TestRPC generaba un conjunto de address, podemos saber cuáles eran con Truffle, para esto escribimos

web3.eth.accounts

Y nos da todas las cuentas, para conocer la nuestra (la que estamos usando actualmente) el comando es

web.eth.coinbase

Y nos da un address, vamos a ponerlo en una variable para que sea más cómodo trabajar

var coinbase = web3.eth.coinbase

Ahora sí, vamos a invocar el contrato, primero lo más simple, conocer el saldo de nuestro address, para ello tenemos que invocar a getBalance y pasar el address que queremos conocer, en este caso el nuestro, así de simple

coin.getBalance.call(coinbase)

y nos retorna el valor, 1000, si consultamos la segunda cuenta en el array vemos que tiene 0

coin.getBalance.call(web3.eth.accounts[1])

Y es cero, en el diccionario interno que mantiene los balances de nuestro contracto el valor para este address es cero, vamos a transferir entonces

mirando el código del sendCoin vemos que recibe dos parámetros, el address de destino y el importe ya que quien genera la transacción (esta información se obtiene del contexto msg.sender) es quien transfiere al destino, en este caso como lo que vamos a hacer es modificar el estado del contrato (estamos alterando el contenido del diccionario que mantiene el balance) se va generar una transacción que tendrá un costo y demorará un tiempo (segundo o minutos dependiendo de la Blockchain y la confirmación de bloques) lo que vamos a hacer es usar sintaxis para leer el valor que retorna la promesa que devuelve la invocación, de este modo

 coin.sendCoin(web3.eth.accounts[1], 150).then(function(tx){console.log(tx)})

Esto demora un instante (en una Blockchain real será mucho más tiempo) y nos muestra el resultado de la transacción, para verificar si la transferencia funcionó consultamos nuevamente el balance de la cuenta destino

truffle(development)> coin.getBalance.call(web3.eth.accounts[1])
{ [String: '150'] s: 1, e: 2, c: [ 150 ] }

y vemos que tiene 150, por supuesto nuestro balance ahora es menor

coin.getBalance.call(coinbase)
{ [String: '9850'] s: 1, e: 3, c: [ 9850 ] }

con esto hemos logrado, compilar, desplegar y probar nuestro contrato, como dije estos nuevos valores (el estado del contrato) ya no se puede alterar, la forma de hacerlo es realizar otra operación que modifique su estado pero quedará todo el historial de cambios, porque es una Blockchain.

De este modo funciona un contrato inteligente, más adelante vamos a ver cómo hacer una aplicación web para acceder al contrato y otras cositas más.

Dejo un link con el ejemplo del contrato simplificado.

Nos leemos.

Patrones útiles en Javascript: Module

Esto es el principio de una serie de posts que podemos llamar “back to basics” o “es bueno saber esto para la vida en general” o “no cazo una en javascript”, como sea, la idea es ir un poco más allá del punto en que el uno es un programador backend y hace Javascript porque lo tiene que hacer y como la sintáxis es similar a Java, C o C# más o menos me las rebusco.

Por ellos veremos algunas “cositas” y particularidades de Javascript y algunos de los patrones que podemos usar para que nuestro código no sea una bolsa de funciones tiradas sin más relación.

Ya he hablado en este blog hace unos años sobre algunas cosas básicas de Javascript como:

Justamente de esto último vamos a hablar hoy, o mas o menos…

El patrón Module

Bien, todos sabemos que hasta hoy no es posible definir niveles de visibilidad en Javascript, es decir, declarar algo como privado y si bien en versiones futuras se podrá no siempre tenemos la suerte de que quienes vayan a usar nuestra aplicación usen un navegador de última generación y lo mismo, no contamos con esa capacidad, pero es posible definir un ámbito de visibilidad acotado para nuestro código, cómo? usando el patrón module.

Básicamente es una función autoejecutable que tiene esta forma

var prueba = (function(){
    // esta variable no es visible fuera de este módulo
    var variablePrivada1 = "hola ";
    // esta función tampoco es visible fuera de este módulo
    var funcionPrivata = function(nombre){
        return variablePrivada1 + nombre;
    };

    // declaramos que esta función retorn un objeto con un único miembro
    // llamado "saludar" que apunta a la función privada "funcionPrivada"
    return {
        saludar: funcionPrivata
    };
})();

Este código es una función autoejecutable, cuyo resultado queda en la variable prueba, ya que la función tiene declaraciones que no son visibles desde el exterior (como variablePrivada1) y al ejecutarse automáticamente, es decir, una vez que el intérprete de Javascript pasa por estas líneas llega a los paréntesis finales y ejecuta la función dejando en la variable prueba lo que la misma retorna, un objeto que no tiene más que un miembro llamado saludar que apunta a la función funcionPrivada.

De este modo ni la variable ni la función son visibles, lo único que quien consume la variable prueba ve es un objeto con una miembro saludar que acepta un parámetro y podemos invocar, el resto está protegido.

Haciendo simplemente

prueba.saludar("Leonardo")

Invocamos el objeto y llamamos a la función privada a través del punto público que nosotros decidimos, de esta manera conseguimos que ciertas partes de nuestro código sean privadas. Además de la elegancia no llenamos todo de cosas públicas, que en caso de trabajar sobre algo ya existente puede generar problemas de “pisar” cosas existentes o que alguien pise las nuestras.

Cumplir con los principios SOLID

Con este simple ejemplo (a pesar de no ser programación orientada a objetos) podemos cumplir con algunos de los principios SOLID

  • Responsabilidad única
  • Principio Abierto / Cerrado (en este caso solo hemos “cerrado)

Si quisiéramos dejar abierto nuestro código para que sus dependencias pudieran ser inyectadas no tenemos más que pasarlas a parámetros, por ejemplo:

var consoleWritter = (function(){
    return {
        write: function(s){
            console.log(s);
        }
    };
})();

var prueba = (function(writter){

    var variablePrivada1 = "hola ";
    var funcionPrivada = function(nombre){
        writter.write(variablePrivada1 + nombre);
    };

    // declaramos que esta función retorn un objeto con un único miembro
    // llamado "saludar" que apunta a la función privada "funcionPrivada"
    return {
        saludar: funcionPrivada
    };

})(consoleWritter);


prueba.saludar("Leonardo");

En este ejemplo declaramos un consoleWritter (que no es más que un wrapper a console.log) y lo inyectamos por medio de parámetro de la función autoejecutable que retorna nuestro objeto prueba, de esta manera hacemos que el objeto prueba no sepa quién se encarga de escribir el mensaje y cumplimos con el principio abierto/cerrado de SOLID y de paso con la inversión de dependencias.

Dejo un link con los dos ejemplos de código en github.

Nos leemos.

¿Qué es TDD?

En Septiembre pasado tuve el honor de dar una charla en Madrid para al comunidad de MSCoders sobre TDD y este post es un poco el resumen de lo expuesto en la misma, que es una mezcla entre lo que la teoría dice del tema muuuyy matizado con mi experiencia y opiniones.

Al principio voy a contar un poco lo de la charla y al final del post el ejemplo, así que si asististe a la charla o sabés del tema te podés saltar todo e ir directo al código.

¿Qué es TDD?

Son las siglas de Test Driven Development, es decir, desarrollo guiado por pruebas, la idea es desarrollar nuestro software comenzando por las pruebas, ya que en principio, sabemos lo que queremos que el software haga, lo que no sabemos es el cómo.

Entonces primero planteamos la prueba que va a validar que nuestro código cumpla con una tarea dada, luego creamos el código que hacer efectivamente la tarea.

Desarrollar software es complejo.

Todos sabemos que el software es complejo, es complejo en sí la tarea de “hacer” el software y es complejo todo lo que lo rodea, los requerimientos que cambian, que son poco claro, que no están del todo definidos y en general el escenario que nos encontramos es una combinación de todo eso, a eso le podemos sumar todo lo inherente al desarrollo, los patrones, las prácticas, las cosas que hay que tener en cuenta para futuras funcionalidades, la limitaciones del lenguaje, las ambigüedades, los casos no planteados, la imposibilidad de comprenderlo todo a la primera, etc.

La agilidad y sus ciclos de feedback

En las metodologías ágiles (como Scrum) la idea es que habiendo mucho que no sabemos la mejor forma de abordarlo es con ciclos de trabajo cortos durante los cuales entregamos algo de valor al final de cada ciclo para obtener una retro-alimentación que nos permita ajustar lo que hacemos y validar los requerimientos, el siguiente ciclo veremos si necesitamos cambiar algo que hicimos o está suficientemente bien, de este modo vamos aprendiendo cada vez más y vamos teniendo siempre algo que mostrar para obtener información.

En cierta forma TDD es similar, la idea es comenzar con las pruebas para ir “descubriendo” el código, en lo relacionado al diseño, y al final de cada test tener una caso funcionando (que es mejorable, por supuesto) pero tenemos feedback que nos permite comprender mejor el problema, la solución y nos da siempre algo que funciona.

El ciclo de TDD

Una de las más importantes de TDD es que el ciclo, que consta de tres partes:

  • Escribir el test y que falle = ROJO
  • Escribir que código que hace que el test pase (cumplir con el requerimiento) = VERDE
  • Hacer refactor (cambiar el código sin que cambie su comportamiento) = AZUL

La idea es centrarnos en una tarea, que funcione, sin ser perfecta la implementación, de este modo evitamos varios de los tantos males del desarrollo de software como ser:

  • Optimizar antes de tiempo
  • Hacer cosas que uno no va a necesitar

El objetivo es hacer solo que es necesario para cumplir con el requerimiento, ni un poco más ni un poco menos, en definitiva es por lo que el cliente se pone contento, que al presionar un botón algo ocurra.

Además nosotros no nos desenfocamos con optimizaciones que no son necesarias ahora mismo (y tal vez nunca lo sean) o por intentar reutilizar código que nunca será reutilizado o por implementar ciertos escenarios que nunca ocurrirán, en resumen, somos más eficientes y entregamos valor al final de cada ciclo que nos permite evolucionar.

El ciclo más o menos es ese gráfico simplificado en los pasos 1, 2 y 5.

Un ejemplo de TDD

Para ejemplificar esto voy a hacerlo a través del código, con un ejercicio muy simple: el String calculador, y como todo tiene una lista de requerimientos:

  1. Create a simple String calculator with a method int Add(string numbers)
    1. The method can take 0, 1 or 2 numbers, and will return their sum (for an empty string it will return 0) for example “” or “1” or “1,2”
    2. Start with the simplest test case of an empty string and move to 1 and two numbers
    3. Remember to solve things as simply as possible so that you force yourself to write tests you did not think about
    4. Remember to refactor after each passing test
  2. Allow the Add method to handle an unknown amount of numbers
  3. Allow the Add method to handle new lines between numbers (instead of commas).
    1. the following input is ok:  “1\n2,3”  (will equal 6)
    2. the following input is NOT ok:  “1,\n” (not need to prove it - just clarifying)
  4. Support different delimiters
    1. to change a delimiter, the beginning of the string will contain a separate line that looks like this:   “/1/[delimiter]\n[numbers…]” for example “//;\n1;2” should return three where the default delimiter is ‘;’ .
    2. the first line is optional. all existing scenarios should still be supported
  5. Calling Add with a negative number will throw an exception “negatives not allowed” - and the negative that was passed.if there are multiple negatives, show all of them in the exception message

Hay algunos más, si quieren verlos este es el link al post original de Roy Osherove

Comenzando con TDD en el código

Vamos a ser totalmente minimalistas con la implementación (como todo en la vida hay gente más y menos extremista, me gusta un poco por el medio pero en este caso como es el primer ejemplo voy a ser más extremista que de costumbre), así que en Visual Studio creamos un proyecto del tipo de Unit Test, y a la clase que nos crea por defecto le vamos a poner un nombre relacionado con lo que estamos haciendo para esto del nombre hay varias opciones:

  • Que se llame StringCalculatorTests
  • Que se llame StringCalculatorFixture
  • Que se llame StringCalculatorShould
  • Etc.

No vamos a discutir ahora por qué una puede ser mejor que otra, vamos a ponerle StringCalculatorShould, y para comenzar con el primer test vamos a tomar el primer requerimiento, ya habrá tiempo de cambiar el nombre a la clase si no nos gusta:

namespace UnitTestProject
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class StringCalculatorShould
    {
    }
}

1.1: Crear un StringCalculador con un método Add que acepte 0, 1 o 2 argumentos y retorne su suma(para un string vacío retornará 0) por ejemplo “” o “1” o “1,2”

Ok, en este caso hay más de un escenario, además de lo más obvio “crear un StrincCalculador con un método Add” nos da 3 escenarios:

  • Al recibir vacío retornará 0
  • Al recibir un argumento retornará el mismo número
  • Al recibir dos números retornará la suma

Entonces, vamos a comenzar con el primero:

“Al recibir vacío retorna 0”

Primero: Escribir el test

Lo primero es definir el test sin la implementación, es decir, del flujo de testing hacer que el test se ponga en rojo.

namespace UnitTestProject
{
    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class StringCalculatorShould
    {
        [TestMethod]
        public void Return_0_When_Receives_Empty()
        {
            var stringCalculator = new StringCalculator();
            var result = stringCalculator.Add("");
            Assert.AreEqual(0, result);
        }
    }

    internal class StringCalculator
    {
        internal object Add(string v)
        {
            throw new NotImplementedException();
        }
    }
}

Vemos varias cosas para señalar: la clase StringCalculator está en el mismo archivo, el parámetro se llama “v”  y las visibilidad de la clase y el método Add es al menos discutible, pero ignoremos todo esto de momento, sigamos adelante, todas estas cosas las vemos cuando llegue el momento del refactor.

Segundo: Hacer que el test pase

Luego hacemos la implementación (el código que hace que el test pase) pero lo más simple posible, es decir, que se ponga verde.

internal class StringCalculator
{
    internal object Add(string v)
    {
        return 0;
    }
}

Sí, es obvio que retornará 0 en éste y en todos los casos, pero está bien, no pensemos en los otros casos, solo en éste, no queremos optimizar antes de tiempo solo queremos cumplir con el caso y con esto es suficiente.

Tercero: Refactor

Una vez que tenemos el test pasando en verde llega el momento del refactor, el refactor no tiene sentido sin test que aseguren que no “rompimos” la funcionalidad, con lo cual nunca vamos a hacer refactor de algo que no tiene test; de modo que ahora que tenemos algo de cobertura de test hacemos refactor.

image


Perfecto, movimos la clase a un archivo propio y tenemos nuestro primer ciclo de TDD completo, sigamos con el siguiente caso.

“Al recibir un argumento retornará el mismo número”

Una vez más nos dedicamos a la implementación más simple, una podría llegar a pensar en qué pasaría si en lugar de un número se ingresara una letra, no? bien, en teoría es un caso que no está planteado así que no nos lo consideraremos ahora, si bien es bueno escribirlo como un caso en alguna parte para no olvidarlo o incluso plantear el nombre del test. De hecho si somos un poco listos nos damos cuenta que hay varios casos que se nos van ocurriendo que no están planteados, por ejemplo si en lugar de un string vacío llega null, o un espacio, lo mismo, lo anotamos o planteamos el test, pero ahora no invertimos esfuerzo en eso.

Bien, entonces implementamos el segundo requerimiento y nos topamos con la implementación del primero, no hay problema, tenemos tests, modificamos el código para considerar esto y ya, como siempre, la implementación más sencilla que funcione, no nos extendemos en nada que no necesitemos ahora (YAGNI)

Una vez que los test están todos en verde, llega el momento del refactor una vez más

image

“al recibir dos números retorna la suma”

Evidentemente, viendo el ejemplo, se plantea que los números se separen con comas, no nos adelantemos a sacar la coma a una variable ni nada de eso, eso lo hacemos en tiempo de refactor o tal vez nunca, ya lo veremos.

image

y continuamos así con el resto de los casos

Algunas reflexiones

IR despacio

Es muy importante hacer lo mínimo necesario para cada caso, no hace nada de nada de posibles casos o escenario, si los encontramos podemos anotarlos o escribir el nombre de los test para no olvidarlo y lo veremos más adelante, se trata de cumplir con los requisitos siendo eficientes con los recursos.

Dejar lo que no suma para más adelante

A todos nos gusta usar patrones y buenas prácticas, todo eso lo podemos hacer con TDD, pero una vez que tenemos test y una cantidad de casos que permitan descubrir si hay algo que reaprovechar o un patrón que usar

Evitar la sobre-arquitectura

Una de las consecuencias es que no nos excedemos con la arquitectura que no vamos a necesitar, algo que a muchos nos ha pasado y nos pasa.

Foco

Una de las ideas del TDD es plantearse una única cosa sencilla en su mínima expresión y nada más, esto nos permite enfocarnos en objetivos alcanzables, mejorando nuestra sensación de que estamos avanzando, mejora nuestra autoestima, nos sentimos productivos, vamos comprendiendo mejor el negocio y todo el tiempo tenemos algo que funciona, esto mejora la productividad y el ánimo.

Podemos hacer TDD o usar algunas de sus prácticas en sistemas legacy?

La respuesta es…depende, como todo en el software, si tenemos muchas dependencias indudablemente tendremos que hacer algo de refactor primero para poder usar fakes o mocks de esas dependencias en nuestros test, para esto es indispensable tener tests primero.

Hacer refactor de código legado para poder construir sobre él de una mejor manera es todo un arte, hay muchas cosas que podemos hacer, pero eso es tema de otro post.

Practicar y practicar más

Me gusta mucho poner el ejemplo del tenis, un tenista de elite (y casi todos) si bien sabe pegar el golpe de revés correctamente, lo practica, incluso más, hay ocasiones en que lo ejecuta de manera diferente dependiendo del tipo de superficie o mejor aún, cambia la técnica para mejorar su juego; mi opinión es que si uno quiere ser un programador de nivel mejor que la media debe hacer lo mismo: practicar las técnicas que utiliza a diario, en este caso TDD hay que practicarlo todo el tiempo hasta que la ejecución nos resulte natural e intuitiva.

Nos leemos.