Artesanía de Software y
Desarrollo Dirigido por Pruebas
¿QUÉ ES LA ARTESANÍA DE SOFTWARE
• No sólo software que funciona,
• No sólo responder al cambio,
• No sólo los individuos y las interacciones,
• No sólo la colaboración con los clientes,
• sino también el software bien
diseñado
• pero también agregar valor de
forma sostenida
• sino también una comunidad de
profesionales
• sino también de asociaciones
productivas
Como aspirantes a artesanos de software, estamos elevando el nivel
de desarrollo de software profesional practicándolo y ayudando a
otros a aprenderlo. A través de este trabajo hemos llegado a valorar:
Es decir, en la búsqueda de los elementos de la izquierda hemos
encontrado que los elementos de la derecha son indispensables.
Manifiesto por la Artesanía de Software
¿Qué es la Artesanía de Software?
• Es acerca de crear conciencia de lo que está mal con
el actual estado de la práctica.
• Es acerca de hacer el mejor trabajo posible.
• Comprometerse con nuestra profesión.
• Sentir orgullo del trabajo bien hecho.
• Mejorar continuamente nuestras habilidades.
• Satisfacer a nuestros usuarios con software de
calidad.
Lo que la Artesanía de Software NO es
Ingeniería de Software:
“La promesa implícita de la ingeniería de software es que si tienes un
proceso sistemático y cuantificado, cualquiera puede desarrollar
software exitosamente.”
-Pete McBreen
Lo que la Artesanía de Software NO ES
• No es solo acerca de “Código Bonito”.
• Darle la espalda a los aspectos de negocio y
concentrarse SOLO en el lado técnico.
• Buscar salidas fáciles, “aceite de víbora” o
“balas de plata”.
• Crear certificaciones artificiales y un modelo
de negocio a su alrededor.
DESARROLLO DIRIGIDO POR PRUEBAS (TDD)
Qué es TDD
“Test-first coding is not a testing
technique”
Ward Cunningham
¿Qué es TDD? (cont.)
• Es tomar la responsabilidad de la calidad de
nuestro código.
• Es entender el código que escribimos en todo
momento, no solo “suponer” que
entendemos.
• Es una habilidad y como tal, requiere práctica
para dominarse.
• Es una disciplina.
El valor de TDD
Tres Reglas(según Robert C. Martin)
• No se permite escribir ningún código de producción sin tener una prueba que falle.
• No se permite escribir más de la prueba que lo necesario para fallar (y no compilar es fallar).
• No está permitido escribir más código de producción que el necesario para pasar la prueba unitaria ACTUAL.
Paso a paso…
from unittest import main, TestCasefrom unittest import main, TestCasefrom unittest import main, TestCasefrom unittest import main, TestCase
class TestPrimeFactors(TestCase):class TestPrimeFactors(TestCase):class TestPrimeFactors(TestCase):class TestPrimeFactors(TestCase):def testPrimesOf0(self):def testPrimesOf0(self):def testPrimesOf0(self):def testPrimesOf0(self):
self.assertEquals([], factorsOf[0])self.assertEquals([], factorsOf[0])self.assertEquals([], factorsOf[0])self.assertEquals([], factorsOf[0])
if __name__ == '__main__':if __name__ == '__main__':if __name__ == '__main__':if __name__ == '__main__':main()main()main()main()
E=================================================== ===================ERROR: testPrimesOf0 (__main__.TestPrimeFactors)--------------------------------------------------- -------------------NameError: global name 'factorsOf' is not defined--------------------------------------------------- -------------------Ran 1 test in 0.001s
FAILED (errors=1)
Paso a paso…
............
def factorsOf(n):def factorsOf(n):def factorsOf(n):def factorsOf(n):return [] return [] return [] return []
............
.
--------------------------------------------------- -------------------
Ran 1 test in 0.000s
OK
Paso a paso…
...
def testPrimesOf0to1testPrimesOf0to1testPrimesOf0to1testPrimesOf0to1(self):self.assertEquals([], factorsOf(0))self.assertEquals([], factorsOf(1))self.assertEquals([], factorsOf(1))self.assertEquals([], factorsOf(1))self.assertEquals([], factorsOf(1))
...
.
--------------------------------------------------- -------------------
Ran 1 test in 0.000s
OK
Paso a paso…
............
def testPrimesOf2(self):def testPrimesOf2(self):def testPrimesOf2(self):def testPrimesOf2(self):self.assertEquals([2], factorsOf(2))self.assertEquals([2], factorsOf(2))self.assertEquals([2], factorsOf(2))self.assertEquals([2], factorsOf(2))
............
.F
=================================================== ===================
FAIL: testPrimesOf2 (__main__.TestPrimeFactors)
--------------------------------------------------- -------------------
AssertionError: Lists differ: [2] != []
--------------------------------------------------- -------------------
Ran 2 tests in 0.029s
FAILED (failures=1)
Paso a paso…
...
def factorsOf(n):
if n > 1:if n > 1:if n > 1:if n > 1:return [n]return [n]return [n]return [n]
return []...
..
--------------------------------------------------- -------------------
Ran 2 test in 0.000s
OK
Paso a paso…
...
def testPrimesOf2to3testPrimesOf2to3testPrimesOf2to3testPrimesOf2to3(self):self.assertEquals([2], factorsOf(2))self.assertEquals([3], factorsOf(3))self.assertEquals([3], factorsOf(3))self.assertEquals([3], factorsOf(3))self.assertEquals([3], factorsOf(3))
...
..
--------------------------------------------------- -------------------
Ran 2 test in 0.000s
OK
Paso a paso…
............
def testPrimesOf2to4testPrimesOf2to4testPrimesOf2to4testPrimesOf2to4(self):self.assertEquals([2], factorsOf(2))self.assertEquals([3], factorsOf(3))self.assertEquals([2,2], factorsOf(4))self.assertEquals([2,2], factorsOf(4))self.assertEquals([2,2], factorsOf(4))self.assertEquals([2,2], factorsOf(4))
............
.F
=================================================== ===================
FAIL: testPrimesOf2to4 (__main__.TestPrimeFactors)
--------------------------------------------------- -------------------
AssertionError: Lists differ: [2, 2] != [4]
--------------------------------------------------- -------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
Paso a paso…...def factorsOf(n):
result, factor = [], 2result, factor = [], 2result, factor = [], 2result, factor = [], 2if n > 1:while n > 1:while n > 1:while n > 1:while n > 1:
return [n]while n % factor == 0:while n % factor == 0:while n % factor == 0:while n % factor == 0:
result.append(factor)result.append(factor)result.append(factor)result.append(factor)n /= factorn /= factorn /= factorn /= factor
factor += 1factor += 1factor += 1factor += 1return []return resultreturn resultreturn resultreturn result
...
..
--------------------------------------------------- -------------------
Ran 2 test in 0.000s
OK
Aprendiendo TDD: Trampas
• Las pruebas se tornan difíciles de escribir, por lo que sentimos una desaceleración importante.
• Corren lentamente, lo que nos volvemos renuentes a ejecutarlas frecuentemente.
• Son frágiles, por lo que cambios aparentemente sin importancia en el código provocan que un montón de pruebas fallen.
• Mantenerlas en forma y funcionando se vuelve complejo y consume tiempo.
Escribiendo pruebas unitarias efectivas
“Las pruebas unitarias deben ser legibles,
confiables y fáciles de mantener”
-Roy Osherove
“The Art of Unit Testing”
Escribiendo pruebas unitarias efectivas:
Legibilidad
• Mis pruebas son tan importantes como el
código de producción.
• Aspirar a que mis pruebas se lean como una
“receta de cocina” (patrón composed method).
• Si no es posible determinar lo que una prueba
está haciendo, es probable que en realidad
esté verificando múltiples cosas.
Escribiendo pruebas unitarias efectivas:
Confiabilidad
• Evita a toda cosa colocar lógica en el código de
una prueba (if-then, switch/case, etc).
• Evita calcular el valor esperado DENTRO de la
prueba.
• Evita compartir estado entre pruebas.
• Usa inyección de dependencias.
Escribiendo pruebas unitarias efectivas:
Mantenibilidad
• Mantén simple el código de inicialización.
• Crea fixtures o incluso casos de prueba especializados para cada escenario.
• Si es necesario, convierte cada escenario en una clase de prueba individual.
• Si una parte del código es particularmente difícil de probar, busca problemas en el diseño del mismo.
Escribiendo pruebas unitarias efectivas:
Rapidez
• Una prueba unitaria efectiva debería
ejecutarse en milisegundos, NO EN SEGUNDOS.
• Si las pruebas no son rápidas, NO SE USARÁN.
• Mantén conjuntos pequeños y bien enfocados
de pruebas, además de la suite global.
Aprendiendo TDD (revisado)
• Comienza con algo sencillo (¡pero no te detengas
ahí!)
• Escribe muchas pruebas (tantas como puedas).
• Familiarízate con el ritmo y las reglas de TDD.
• Cuando encuentres algo que no sabes como
probar, apóyate en un compañero.
• Nunca dejes de aprender.
Bibliografía
• “The Clean Coder” de Robert C. Martin.
• “Test Driven Development: By Example” de Kent Beck.
• “The Art of Unit Testing” de Roy Osherove.
• “Growing Object-Oriented Software, Guided by Tests” de Steve Freeman y Nat Pryce.
• “Agile Java: Crafting Code with Test-Driven Development”de Jeff Langr.
• “Diseño Ágil con TDD” de Carlos Ble Jurado -> !Es gratis! http://www.dirigidoportests.com/el-libro
• Twitter: @alfredochv
• Blog: http://pensamientoobjetivo.blogspot.mx