Una sinfonía en C#

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

¿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.

¿Cómo hacer un test de HttpClient?

Cuando hacemos prueba unitarias (AKA unit testing) uno de los objetivos es aislarnos de los agentes externos para crear un ambiente controlado y que nuestros test se ejecuten bajo condiciones repetibles. Bien, dicho esto uno de las clases que al menos yo me encuentro más seguido es HttpClient, que no hace otra cosa que hacer llamadas HTTP, por desgracia al día de hoy no existe una interfaz para usarla para hacer un mock, entonces tenemos que echar mano de cosas como ésta:

Primera aproximación, hacer un wrapper:

public class HttpClientWrapper
{
    public Uri BaseAddress { get; set; }
    public HttpRequestHeaders DefaultRequestHeaders { get; }
    public TimeSpan Timeout { get; set; }
    public long MaxResponseContentBufferSize { get; set; }

    public void CancelPendingRequests();
    public Task DeleteAsync(string requestUri);
    public Task DeleteAsync(string requestUri, CancellationToken cancellationToken);
....

Algo así pero bien, copiar toooodos los métodos que queremos usar (pero marcados como virtuales) y tener dentro una instancia del verdadero HttpClient a la cual simplemente llamamos en cada uno de nuestro métodos, entonces cuando queremos probar simplemente hacemos un mock donde sobre-escribimos los método que queremos probar.

var mocked = new Mock();
var client = mocked.Object;

mocked.Setup(i => i.SendAsync(It.IsAny())).Returns(Task.FromResult( new HttpResponseMessage()));

Como imaginamos hacer un wrapper suena a mucho trabajo y, seamos realistas, es poco elegante.

Opción dos, heredar HttpMessageHandler

Algo que aprendí hace poco es la existencia de la clase HttpMessageHandler, que es internamente utilizado por HttpClient para hacer las llamadas, y además se puede sobre-escribir.

Entonces, podemos hacer dos cosas, primero crear una versión para probar, simplemente una clase para testing, que nos permita configurar cómo deber responder y después simplemente inyectar la clase en el constructor de HttpClient

public class OffLineResponseHandler : DelegatingHandler
{
    private readonly Dictionary responses = new Dictionary();
    public void AddResponse(Uri uri, HttpResponseMessage responseMessage)
    {
        responses.Add(uri, responseMessage);
    }
    public void AddOkResponse(Uri uri, string content)
    {
        var message = new HttpResponseMessage(HttpStatusCode.OK);
        message.Content = new StringContent(content);
        responses.Add(uri, message);
    }
    public void AddOkResponse(Uri uri)
    {
        this.AddOkResponse(uri, string.Empty);
    }
    public void AddServerErrorResponse(Uri uri)
    {
        var message = new HttpResponseMessage(HttpStatusCode.InternalServerError);
        responses.Add(uri, message);
    }
    public void RemoveAll()
    {
        this.responses.Clear();
    }
    protected async override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        if (responses.ContainsKey(request.RequestUri))
        {
            return responses[request.RequestUri];
        }
        else
        {
            return await Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request });
        }
    }
}

Esta solución es bastante elegante, permite configurar respuestas con los métodos AddOkResponse y AddServerErrorResponse se agregan a un diccionario que permite grabar respuestas, entonces cuando queremos probar hacemos algo así:

[TestMethod]
public void AddOkResponse()
{
    var target = new HttpClientTest.OffLineResponseHandler();
    var url = new Uri("http://www.fake.com");

    var client = new System.Net.Http.HttpClient(target);

    target.AddOkResponse(url);

    var result = client.GetAsync(url).Result;

    Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
}

En este ejemplo inyectamos el OfflineResponseHandler y lo inyectamos en el constructor de HttpClient, por otro lado configuramos las respuestas que queremos que de dependiendo de la URL.

La otra opción (es menos trabajo) es una clase que herede de HttpMessageHandler con los métodos Send y SendAsync marcados como virtuales para poder usar un framework de mocking del siguiente modo:

public class FakeHttpMessageHandler : HttpMessageHandler
{
    public virtual HttpResponseMessage Send(HttpRequestMessage request)
    {
        throw new NotImplementedException("Mock this please");
    }

    protected override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        return Task.FromResult(Send(request));
    }
}

Entonces cuando queremos probar con simplemente configuramos el framework de mocking así:

var fakeHttpMessageHandler = new Mock { CallBase = true };

fakeHttpMessageHandler.Setup(f => f.Send(It.IsAny())).Returns(new HttpResponseMessage
{
    StatusCode = HttpStatusCode.OK,
    Content = new StringContent("Hi!")
});

Y listo, mismo efecto, simplemente inyectamos en el constructor del HttpClient y tirar millas, podemos probar con bastante facilidad.

Dejo un link con el código de ambas utilidades, nos leemos.

¿Qué es un circuit breaker?

En este post voy a contar de qué se trata el patrón circuit breaker para utilizar cuando dependemos de recursos externos y vamos a ver un ejemplo de implementación en C#.

Objetivo

El objetivo de este patrón es poder manejar un error al cosultar un servicio remoto que puede tomar un tiempo indeterminado en recupearse. La idea es mejorar la capacidad de nuestra aplicación a ser resilente a fallos.

Cuando nuestra aplicación consume un servicio externo puede ocurrir que este servicio sufra problemas momentaneos o que tengamos problemas de latencia o conectividad, en general estos problemas se pueden manejar utilizando el patrón retry, sin embargo si el problema no se resuelve rápidamente ésta no es siempre la mejor estrategia.

Entonces el circuit breaker evita que nuestra aplicación consulte un recurso externo (otra aplicación, un servicio, una base de datos, etc.) que muy probablemente nos de error por un tiempo hasta que se estabilice.

En resumen el circuit breaker se utilizará donde no tenga sentido seguir reintentando que el recurso externo responda y el patrón retry no tendía utilidad y por supuesto trasladar los errores causados por la falla del recurso externo (o la espera a que responda) a nuestra aplicación nos traería otro tipo de problemas como por ejemplo operaciones de que se quedan esperando y consumiendo hilos de ejecución, memoria y potencialmente causando más problemas a nuestra aplicación e incluso podamos empeorar la situación del servicio externo no dándole oportunidad de recuperarse si lo continuamos consultando.

Por último el circuit breaker actuará como un proxy sobre las operaciones que queramos manejar con él, esto por supuesto puede penalizar levemente la performance.

Implementando un circuit breaker con C#

Básicamente el circuit breaker es una máquina de estados que puede exponer los siguientes estados:

  • Open: El recurso no está disponible porque se alcanzó un número de fallas en cierto tiempo.
  • Half-Open: Estando en Open ha transcurrido un tiempo dado y pasa a este estado, si se puede acceder al recurso se pasa a Closed, si falla se vuelve a Open y se pone el contador en cero
  • Closed: el circuito funciona, en caso de detectarse una falla se incrementa un contador, si se alcanza un número determinado en un periodo de tiempo dado se cambiar a Open

Por otro lado tendremos al menos dos parámetros

  • Threshold: La cantidad de veces que aceptamos que el recurso externo de error antes de considerarlo en problemas.
  • Reset timeout: El tiempo que se esperará antes que volver a intentar consultar el servicio (el tiempo que le daremos a que se recupere)

 

image

Implemetación simple en C#

Acá dejo una implementación simple den C# utilizando una máquia de estados clásica, esto se puede evolucionar mucho por supuesto.

public interface ICircuitBreaker
{
    /// <summary>
    /// Obtiene la cantidad de fallas aceptadas hasta pasar a estado Open
    /// </summary>
    int Threshold { get; }

    /// <summary>
    /// Obtine el tiempo de espera para pasar a estado Half-Open
    /// </summary>

    TimeSpan Timeout { get; }
    /// <summary>
    /// Obtiene el estado actual
    /// </summary>

    CircuitBreakerState CurrentState { get; }
    /// <summary>
    /// Permite invocar una acción utilizado el circuit breaker
    /// </summary>
    /// <param name="protectedCode">La acción que será invocada</param>
    /// <returns>El estado resultante</returns>
    CircuitBreakerState AttemptCall(Action protectedCode);
}

Esta sería la interfaz de nuestro circuit breaker, es sencilla y se puede mejorar mucho pero sirve para el ejemplo, vamos a la implementación

public enum CircuitBreakerState
{
    Close,
    Open,
    HalfOpen
}

public class SimpleCircuitBreaker : ICircuitBreaker
{
    private int threshold;
    private TimeSpan timeout;
    private int attemptCounter;
    private DateTime failureTime;
    private CircuitBreakerState currentState = CircuitBreakerState.Close;

    public int Threshold => this.threshold;
    public TimeSpan Timeout => this.timeout;
    public CircuitBreakerState CurrentState => this.currentState;

    public SimpleCircuitBreaker(int threshold, TimeSpan timeout)
    {
        this.threshold = threshold;
        this.timeout = timeout;
    }

    public CircuitBreakerState AttemptCall(Action protectedCode)
    {
        switch (this.currentState)
        {
            case CircuitBreakerState.Close:

                try
                {
                    protectedCode();
                }
                catch (Exception)
                {
                    this.attemptCounter++;
                    if (this.attemptCounter > this.threshold)
                    {
                        this.failureTime = DateTime.Now;
                        this.attemptCounter = 0;
                        this.currentState = CircuitBreakerState.Open;
                    }
                }

                break;
            case CircuitBreakerState.Open:
                if (this.failureTime.Add(this.timeout) > DateTime.Now)
                {
                    this.currentState = CircuitBreakerState.HalfOpen;
                }
                break;
            case CircuitBreakerState.HalfOpen:
                try
                {
                    protectedCode();
                }
                catch (Exception)
                {
                    this.failureTime = DateTime.Now;
                    this.attemptCounter = 0;
                    this.currentState = CircuitBreakerState.Open;
                }
                break;
        }
        return this.currentState;
    }
}

La forma de utilizar el código es crear una instancia del SimpleCircuitBreaker y llamar al recurso externo a través del método AttenptCall.

Como vemos una máquina de estados que inicia en Closed y en caso de error incrementa un contador, si se llega el número de fallos permitos (threshold) pasa a Open, si se ha pasa un tiempo dado (el que consideramos que es adecuado para que el recurso externo se recupere) se pasa al Half-Open, si se intenta acceder nuevamente y falla se vuelve a Open, si funciona a Close.

De esta manera todo el código que dependa de recursos externos puede conocer el estado del recurso y esperar para llamarlo en caso que se encuentre en fallo.

Como dije se puede mejorar mucho este código no tiene sentido para explicar el concepto hacerlo ahora.

Les dejo un link con el código en github.

Nos leemos.

Javascript: Arrow functions

Arrow function es otra de las novedades de Ecma Script 6 y para quienes usamos C# nos va a resultar familiar la sintaxis.

Básicamente nos permite expresar cualquier función usando una flecha ( => ) hasta ahora podíamos hacer esto:

 

[1,2,3,4,5,6,7,8].filter(function(item) { 
  return item > 4 ;
});

con ES6 se puede hacer así:

[1,2,3,4,5,6,7,8].filter( item => { 
  return item > 4;
});

En definitiva no es más que una forma corta de declarar una función anónima.

Parámetros

Con respecto a los parámetros podemos hacer varias cosas dependiendo de si tenemos parámetros, o cuántos tengamos uno o muchos, por ejemplo.

cuando no tenémos  parámetros podemos poner los dos paréntesis o un guión bajo.

var a = () => { console.log("hola");}
var a = _ => { console.log("hola”);}

Cuando tenemos un único parámetro podemos usar o no los paréntesis

var a = (y) => { return y };
var a = y => { return y };

En cuando a varios parámetros siempre usamos los paréntesis

var a = (x,y) => { return x + y;}

Retorno implícito

En ciertas circunstancias las arrow functions crean el return por nosotros, es decir, no siempre es requerido utilizar return

var a = (x, y) => x + y;

Es equivalente a

var a = (x, y)=> { return x + y; }

En resultado

Usando arrow functions el primer ejemplo quedaría así:

[1,2,3,4,5,6,7,8].filter(item => item > 4)

Nos leemos.

Material de mi charla sobre Blockchain, Smart Contracts y Azure

El pasado Miércoles 24 de Mayo tuve el honor de una vez más participar de SGVirtual, en este caso con una charla sobre Blockchain, Smart Contracts y Azure.

El charla fue grabada como hace siempre la gente de Software Gurú.

 

El material que utilicé se puede bajar de mi repo de Github

Algunos links sobre el tema:

Nos leemos.