4. Software testing - Introducción
¿Qué son las pruebas de software?
Forma automatizada de probar, empíricamente, que un software funciona
como se espera.
¿Qué ganamos realizando pruebas de software?
Fiabilidad, consistencia, eficiencia, mantenibilidad, mejor software y facilidad
para el refactoring. Pero sobre todo, dormir mucho mejor.
¿Qué precio debemos pagar?
• Diseñar nuestro software para que sea testable facilmente (altamente
cohesionado, bajamente acoplado, etc).
• Crear y mantener nuestros tests como si fuese código de producción.
Class Cohesion Revisited: http://www.iro.umontreal.ca/~sahraouh/qaoose/papers/Kabaili.pdf
5. Software testing - Tipos de Test
• Tests unitarios
Comprueban el correcto funcionamiento de la mínima unidad
de código posible (funcionalmente significativa).
• Tests de integración
Comprueban la interacción entre distintas entidades de nuestro
software.
• Tests de regresión
Aseguran la inmutabilidad del funcionamiento anterior a una
modificación.
• Tests de Sistema (end-to-end)
Prueban completamente un sistema integrado.
• Tests de aceptación
Prueban la funcionalidad (habitualmente desde el punto de vista
del usuario final).
6. Software testing - ¿Por qué es importante?
http://www.quora.com/What-is-software-unit-testing-and-why-is-it-important
8. Pruebas unitarias - Introducción
Tests unitarios
• Muy útiles por el alcance de su prueba (muy concreto).
• Imprescindibles para el practicante de TDD
• Solo comprobarán uno de los comportamientos de un método de una clase.
• Suelen ser tests de “caja blanca”.
• Cumpliran los principios FIRST.
9. Pruebas unitarias - FIRST
Principios FIRST Descritos por Robert C. Martín (Uncle Bob) en su libro Clean Code.
Fast Deben ejecutarse muy rápido.
Isolated Único motivo de fallo, independientes del resto de test.
Repeatable Pueden ejecutarse n veces, siempre el mismo resultado.
Self-validating Resultado booleano y automático.
Timely Construido en el momento oportuno (en TDD antes del código)
10. Pruebas unitarias – Organización de proyectos
Organización de proyectos de Testing
• El proyecto de testing estará separado
del código de producción.
• Cada clase de test tendrá
correspondencia con una clase de
producción.
12. Pruebas unitarias – Ejecución de test
Desde el Test Explorer
• Denominados Test Runners
13. Pruebas unitarias – Partes de un test Unitario
• Arrange
Preparación del escenario a testear.
• Act
Acto de ejecución del código que va
a devolver el resultado a testear.
• Assert
Chequeo del resultado con las
comprobaciones de resultados.
// Arrange
MyProductionClass myProductionClass = new MyProductionClass();
// Act
bool result = myProductionClass.GetBooleanResult();
// Assert
Assert.IsTrue(result);
16. • Cuantas veces:
• Has empezado a desarrollar con especificaciones confusas
• Has descubierto, demasiado tarde, fallos en la especificación
• Has tenido un código totalmente fiable justo en el momento de terminarlo
• Has desarrollado más código del necesario
• Que nivel de estrés
• Te produce tocar un código desconocido
• Y un código de altas implicaciones/afecciones.
TDD - Introducción
17. TDD - Introducción
¿Qué ocurre cuando el nivel de estrés se eleva?
Circulo de retroalimentación:
“Kent Beck: Test Driven Development by Example”
18. ¿Qué es? ¿ Cómo se hace? ¿Cómo me beneficia?
TDD
19. TDD - ¿Qué es?
• Escribir tests antes de escribir el código
• Testear antes como una actividad de diseño de software
• No testeamos para validar código, testeamos para diseñarlo
• Testear antes para evidenciar/clarificar qué debe hacer el código
20. TDD - Pasos
¿Qué queremos que haga el código que vamos a desarrollar?
¿Qué hechos demostrarán que nuestro código funciona?
21. TDD - Pasos
El test fallará
porque la función
todavía no existe
¿Escribimos el test?
Pensamos en el comportamiento del código y su interface público
22. TDD - Pasos
Escribimos el código de producción
SOLO el código necesario para pasar el test
23. TDD - Pasos
Refactorizamos la funcionalidad desarrollada
Durante la refactorización NO DEBEMOS cambiar la semántica (sentido)
24. TDD - Beneficios
• Código bajo completa especificación
• Ausencia de YAGNI (You aren’t gonna need it)
• KISS (Keep it simple, stupid). Código más simple y funcional. Se
persigue el objetivo
• Facilidad para aplicar principios SOLID
• Disminución del estrés, aumento de la confianza.
27. Patrones para un buen TDD – Test List
• Realizar una lista de tests
• por funcionalidad/tarea/historia que debemos realizar
• con lo que debe ser y, también, lo que no debe ser
• si surgen nuevos tests o dudas, apuntarlos en la lista
28. Patrones para un buen TDD – Test First
• Escribir los tests antes del código
• romper el circulo de retroalimentación
• si se empiezan a escribir los tests después, se dejará de hacer
tests-first completamente.
• ayuda a definir:
• cual es la respuesta correcta
• cómo voy a chequearla
• donde pertenece esta funcionalidad
• como debería llamarla
29. Patrones para un buen TDD – Assert First
• Escribe tu Assert lo primero de todo
01 [TestMethod]
02 public void CarTravelDistanceTest()
03 {
04 Assert.IsTrue(car.IsPowerOff());
05 Assert.Equals(300, distance.FromStart());
06 }
Queremos comprobar que el cuentakilómetros de un coche
mide correctamente.
Para obtener la medición final, el vehículo debe estar
apagado.
Recorremos una distancia concreta y obtenemos esa
distancia.
30. Patrones para un buen TDD – Assert First
• Escribe tu Assert lo primero de todo
• crece según sea necesario para alcanzar el objetivo
01 [TestMethod]
02 public void CarTravelDistanceTest()
03 {
04 Distance distance = car.DistanceFromStart();
05 Assert.IsTrue(car.IsPowerOff());
06 Assert.Equals(300, distance.Kilometers());
07 }
¿De donde sale la distancia?
Del cuenta kilómetros del propio vehículo, desde luego
31. Patrones para un buen TDD – Assert First
• Escribe tu Assert lo primero de todo
• crece según sea necesario para alcanzar el objetivo
¿Y el vehículo?
Lo creamos y debemos conducirlo a donde queremos ir
01 [TestMethod]
02 public void CarTravelDistanceTest()
03 {
04 Car car = new Car("Opel", "blue");
05 car.DriveTo("Bilbao");
06 Distance distance = car.DistanceFromStart();
07 Assert.IsTrue(car.IsPowerOff());
08 Assert.Equals(300, distance.Kilometers());
09 }
32. Patrones para un buen TDD – Test Data
• Usa información que sea fácil de leer
• Recuerda que escribes tests para una audiencia
• Minimiza los datos que usas
Si una lista de 3 elementos es suficiente, no pongas 10
• Como alternativa al “Test Data” está el “Realistic Data”, donde
usamos datos del mundo real.
Sistemas de tiempo real
Buscas equiparar salidas del sistema actual con el sistema
anterior
Estás refactorizando una simulación y se espera la misma
respuesta.
33. Patrones para un buen TDD – Evident Data
• Usa datos evidentes
• Estás escribiendo tests para otros, no para el ordenador
• Deja tantas pistas como sea posible
• Es posible que alguien esté leyendo tus tests el año que viene
• Y es posible que seas tu mismo
• Es preferible explicitar los cálculos:
Esto:
Assert.Equals(300, distance.Kilometers());
Es peor que esto:
Assert.Equals(1300 - 1000, distance.Kilometers());
Y peor que esto:
Assert.Equals(PUNTO_KILOMETRICO_FINAL – PUNTO_KILOMETRICO_INICIAL, distance.Kilometers());
35. Patrones de barra roja – Starter Test
• ¿Con cual de los tests de tu lista deberías empezar?
• empieza por testear una variante de una operación que no haga nada
• a menudo un Starter Test es un test de alto nivel, como un test de
aplicación
var allAccounts = new IDictionary<Company, IList<Accounts>>();
Assert.AreEqual( 0, allCounts.SumAllAccounts());
36. Patrones de barra roja – One Step Test
• ¿Cual de los tests de tu lista es el siguiente a implementar?
• uno que te enseñe algo del sistema
• sepas que puedes implementar sin problemas
• represente un paso hacía el objetivo global
No se sigue un desarrollo top-down o bottom-up.
Más bien se sigue un proceso de lo conocido a lo desconocido. Aprendiendo
por el camino.
37. Patrones de barra roja – Regression Test
• ¿Qué es lo primero que haces cuando un bug/defecto
es reportado?
• escribe el menor test posible que falle por ese motivo
• una vez ejecutado el test y comprobado su fallo,
reparamos el bug
38. Patrones de barra roja – Explanation Test
• ¿Cómo difundir el uso de tests automatizados?
• pide y da explicaciones en términos de tests
“Veamos si lo he entendido. Por ejemplo si tenemos Foo con este valor y
Bar con este otro, la respuesta debería ser X”
40. Patrones de barra verde – Fake it (‘Til you make it)
• ¿Cuál es tu primera implementación para arreglar un test roto?
• devuelve justo lo necesario para arreglarlo y nada más
• seguramente una constante es suficiente. Gradualmente se
convertirá en una expresión u operación que usará variables =
abstracción.
Fake it causa mucha fricción en las personas. Sobre todo en programadores
experimentados.
¿Por qué hacer algo que sabes que tendrás que quitar?
¿Por qué hacer código que no durará ni una hora?
Porque es vital obtener un verde rápidamente y es mejor tener algo
funcionando que no tener nada o algo que falla.
41. Patrones de barra verde – Triangulate
• ¿Cuánto de conservador debo ser con la abstracción de los tests?
• abstrae solo cuando tengas dos o más ejemplos
Cuidado con entrar en lo que Kent Beck llama el bucle de triangulación.
[TestMethod]
public void TestSum()
{
Assert.Equals(4, Sum(3, 1));
}
private int Sum(int x, int y)
{
return 4
}
[TestMethod]
public void TestSum()
{
Assert.Equals(4, Sum(3, 1));
Assert.Equals(5, Sum(3, 2));
}
private int Sum(int x, int y)
{
return x + y;
}
[TestMethod]
public void TestSum()
{
Assert.Equals(4, Sum(3, 1));
Assert.Equals(5, Sum(3, 2));
}
private int Sum(int x, int y)
{
return x + y;
}
42. Patrones de barra verde – Obvious Implementation
• ¿Cómo implementas operaciones que, para ti, son simples?
• simplemente impleméntalas
Fake it y Triangulation son patrones de muy pequeños pasos.
Algunas veces ya sabes cómo se implementa una operación. Adelante!!!
Pero permanece atento a cuan a menudo recibes sorpresas en forma de
barras rojas usando Obvious y, si son demasiadas, vuelve al origen.
Ten en cuenta que Obvious es una “segunda marcha”. Vuelve a la primera en
cuanto “muerdas más de lo que puedes tragar”.
43. Patrones de barra verde – One to Many
• ¿Cómo implementas una operación que trabaja con colecciones?
• prepara el test y ponlo verde, sin la colección
01 [TestMethod]
02 public void SumTest()
03 {
04 var result = Calculations.Sum(5);
05 Assert.AreEqual(5, result);
06 }
01 public static int Sum(int number)
02 {
03 return number;
04 }
44. Patrones de barra verde – One to Many
• ¿Cómo implementas una operación que trabaja con colecciones?
• prepara el test y ponlo verde, sin la colección
01 [TestMethod]
02 public void SumTest()
03 {
04 var result = Calculations.Sum(5, new int[] { 5 });
05 Assert.AreEqual(5, result);
06 }
01 public static int Sum(int number, int[] values)
02 {
03 return number;
04 }
45. Patrones de barra verde – One to Many
• ¿Cómo implementas una operación que trabaja con colecciones?
• prepara el test y ponlo verde, sin la colección
01 [TestMethod]
02 public void SumTest()
03 {
04 var result = Calculations.Sum(5, new int[] { 5 }));
05 Assert.AreEqual(5, result);
06 } 01 public static int Sum(int number, int[] values)
02 {
03 int result = 0;
04 for (int i = 0; i < values.Length; i++)
05 {
06 result += values[i];
07 }
08 return result;
09 }
46. Patrones de barra verde – One to Many
• ¿Cómo implementas una operación que trabaja con colecciones?
• prepara el test y ponlo verde, sin la colección
06 [TestMethod]
07 public void SumTest()
08 {
04 var result = Calculations.Sum(new int[] {3, 2 }));
05 Assert.AreEqual(5, result);
06 }
01 public static int Sum(int[] values)
02 {
03 int result = 0;
04 for (int i = 0; i < values.Length; i++)
05 {
06 result += values[i];
07 }
08 return result;
09 }
Estos patrones que describen TIPOS de tests:
son sobre CUANDO y DONDE escribes tus tests y también CUANDO PARAR de escribir tests.
Un test rojo debe considerarse como una situación de alarma de nivel 1.
Necesitas centrar toda tu atención en solucionarlo.
Usa estos patrones para conseguir el resultado verde más rápido que sea posible incluso si ese resultado no es algo con lo que quieras convivir ni una hora