Aprendiendo a usar Dobles de Prueba

2015-06-22

Esta semana he tenido la suerte de comenzar un proyecto con la ayuda de Modesto San Juan, este es mi primer proyecto real en el cual uso dobles de prueba en los tests, nunca los había usado más allá del contexto de una kata. Modesto me ha ayudado a entender cómo funciona y me ha dado un par de consejos que me serán muy útiles, de hecho gracias a ellos ya no me siento tan perdido a la hora de usar estas herramientas.

En este post hablaré más de los stubs, ¿Por qué usamos stubs? usamos un stub para despreocuparnos de la implementación de un colaborador pero que nos interesa que nos devuelva algo cuando lo llamamos. De esta manera conseguimos testear el comportamiento concreto de la clase en cuestión con un dato ofrecido por el colaborador sin necesidad de currarnos la lógica del este último.

El primer problema con el cual me encontré a la hora de escribir un test de este tipo fue que no supe por dónde empezar. Debido a mi falta de experiencia con este tipo de tests no sabía que preguntas plantearme, que orden seguir, o más genéricamente cómo analizarlo. Modesto entonces intervino y me dijo que intentara seguir la estructura Given, When, Then para plantear el test.

1
2
3
Dada la id "12345" de un producto
Cuando se pide al sistema el producto asociado a dicha id
Entonces el sistema devolverá el producto con id "12345"

Me surgía también la duda de que podía ser falso y qué no, no me resultaba tan evidente. Para ello recurrimos a dibujar en la pizarra un diagrama de secuencia que representaba las llamadas que se sucederían durante el proceso. En el caso anterior tenemos un usuario que conoce una “id” de un producto y va a solicitarle a un servicio el producto asociado a esa id, este servicio recibirá ese id se lo solicitará a nuestro repositorio de productos y una vez obtenido el producto se lo devolverá al usuario. Esto expresado con el diagrama quedaría algo así:

DSC_0045

La clase cuyo comportamiento queremos probar es el Servicio, del esquema podemos deducir que debemos probar 2 cosas: que se ha llamado al método del repositorio que se encargará de buscar el producto y que el producto devuelto por el servicio es el que tiene la id solicitada. Lo que le llega al servicio tanto del repositorio como del usuario, pueden ser valores predeterminados (“falsos”) ya que lo que queremos comprobar es que nuestro servicio hace bien el trabajo que tenga que hacer con estos valores. Es irrelevante cómo el repositorio encuentra ese producto, el cómo lo obtenga el repositorio queda fuera del propósito de este test. Por ello nuestro repositorio será un Stub, que nos devolverá un producto “falso” que cumple con lo que esperamos. No sé si me estoy explicando muy bien, aquí pongo un ejemplo de cómo podría quedar el test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Creamos nuestro productRepository que es un Stub
var productRepository = new Substitute.For<ProductRepository>();
//Cuando llamemos al método Load() con el parámetro "12345" devolverá un
//producto nuevo con dicho id
productRepository.Load("12345").Returns(
new Product("12345");
);
var productService = new ProductService(ProductRepository);
//Given
var id = "12345";
//When
var product = productService.GetProductById(id);
//Then
product.Id.Should().Be("12345");

Conclusión

Puede ser que estos ejemplos sean muy sencillos y no se vea el potencial de éstas ayudas de las que hablo (aunque a mí sí que me han sido útiles en casos así, probablemente por mi inexperiencia). Pero imaginemos un diagrama en el que en lugar de 3 agentes participan 7, en estos casos creo que viene muy bien tener el esquema para poder analizar con claridad el problema.

La verdad que esta semana he entendido mejor bajo qué circunstancias usar dobles de prueba. Me está encantando entender esta otra forma de escribir los tests, además siento que he encontrado otro punto por el cuál seguir practicando para así mejorar mi uso y entendimiento del TDD y gracias a ello dar un pasito más en mi camino hacia convertirme en Software Craftsman.