TDD: Cómo escribir código testeable.<br />
¿Hacer tests es bueno?<br />
¿Por qué no los hacemos?<br />
Razones<br />Válidas<br />¡No se!<br />Código antiguo<br />UI<br />No hacer pruebas<br />Inválidas<br />El diseño es malo<...
Hacer tests es una habilidad<br />
Entonces…<br />¿Cómo se escribe código difícil de probar?<br />
¿Por qué?<br />Construcción de objetos<br />	Trabajo en el constructor<br />	Estados globales<br />	Violaciones de la Ley ...
Localización del “new”<br />Estímulo<br />API de Pruebas<br />Clase bajo prueba<br />Verificaciones<br />
Localización del “new”<br />Otra clase<br />Estímulo<br />Otra clase<br />API de Pruebas<br />Clase bajo prueba<br />Verif...
Localización del “new”<br />Otra clase<br />Otra clase<br />Otra clase<br />Otra clase<br />Estímulo<br />Otra clase<br />...
Localización del “new”<br />Otra clase<br />Otra clase<br />Otra clase<br />Otra clase<br />Estímulo<br />Otra clase<br />...
Localización del “new”<br />Clase Falsa<br />Estímulo<br />Clase Falsa<br />API de Pruebas<br />Clase bajo prueba<br />Cos...
Localización del “new”<br />Lógica de Negocio<br />Construcción del <br />Grafo de Objetos y <br />Búsqueda<br />
Localización del “new”<br />Lógica de Negocio<br />Construcción del <br />Grafo de Objetos y <br />Búsqueda<br />
Localización del “new”<br />Clase Falsa<br />Estímulo<br />Clase Falsa<br />API de Pruebas<br />Clase bajo prueba<br />Ver...
Ejemplo (House)<br />
House<br />classHouse {<br />Kitchenkitchen = new Kitchen();<br />Bedroombedroom;<br />publicHouse() <br />	{<br />this.be...
House Test<br />classHouseTests {<br />	@Test<br />publicvoidimpossibleTestOnIsolation() {<br />		// Can’treplaceanything!...
Análisis<br />	Fácil de instanciar pero…<br />		No se puede remplazar nada.<br />	Puede ser costoso computacionalmente<br ...
House (Refactoizado)<br />classHouse {<br />Kitchenkitchen;<br />Bedroombedroom;<br />@Inject<br />publicHouse(Kitchenkitc...
House Test (Refactorizado)<br />classHouseTests {<br />	@Test<br />publicvoideasierToTestOnIsolation() {<br />DummyKitchen...
Ejemplo (Gardener)<br />
Gardener<br />class Garden {<br />Gardenerjoe;<br />public Garden(Gardenerjoe) {<br />joe.setWorkHours(new TwelveHours());...
Análisis<br />	Mejor pero… <br />		No se pueden remplazar las botas o el 	horario.<br />	La prueba puede tardar (horario d...
Gardener (Refactored)<br />class Garden {<br />Gardenerjoe;<br />	@Inject<br />public Garden(Gardenerjoe) {<br />	this.joe...
¿Por qué?<br />Construcción de objetos<br />Trabajo en el constructor<br />	Estados globales<br />	Violaciones de la Ley d...
Costo de la construcción<br />	Para testear, primero hay que instanciar pero…<br />El trabajo dentro del constructor no ti...
Ejemplo (Super Car)<br />
Super Car<br />class Car {<br />Engineengine;<br />public Car(Filefile) {<br />String m = readModelFromCfg(file);<br />eng...
Super Car Test<br />classCarTest {<br />@Test<br />publicvoidhardToSetupTest() {<br />Filefile = new File(“custom.conf”);<...
Análisis<br />	Pasamos un File cuando lo que se necesita es un Engine.<br />	Toda prueba que necesite Car requiere eta par...
Super Car (Refactorizado)<br />class Car {<br />Engineengine;<br />@Inject<br />public Car(Engineengine) {<br />this.engin...
Super Car (Provider)<br />classCarEngineProvider {<br />@Provides<br />EnginegetEngine(EngineFactoryfactory, @EngineModelS...
Super Car Test (Refactorizado)<br />classCarTest {<br />@Test<br />publicvoideasyToSetupTest() {<br />Engineengine = new F...
¿Por qué?<br />Construcción de objetos<br />	Trabajo en el constructor<br />	Estados globales<br />	Violaciones de la Ley ...
Locura<br />
Locura<br />Definición:<br />Repetir una misma acción una y otra vez y esperar obtener un resultado diferente.<br />Albert...
O mejor dicho… Estados Globales<br />class X {<br />public X() {<br />}<br />publicintdoSomething() {<br />	//… Something…...
Estados Globales<br />int a = new X().doSomething();<br />int b = new X().doSomething();<br />
Estados Globales<br />¿ a == b ó a != b ?<br />
Estados Globales<br />X x1 = new X();<br />Y<br />Q<br />X<br />Z<br />
Estados Globales<br />X x1 = new X();<br />X x2 = new X();<br />Y<br />Q<br />X<br />Z<br />Y<br />Q<br />X<br />Z<br />
Estados Globales<br />X x1 = new X();<br />x1.doSomething();<br />A == B ó A != B<br />X x2 = new X();<br />x2.doSomething...
Consecuencias<br />	Múltiples ejecuciones<br />	Resultados inesperados<br />	Orden importa<br />	No se pueden ejecutar en ...
Estados Globales Ocultos<br />System.currentTime()<br />new Date()<br />Math.random()<br />
Consecuencias<br />	La API miente:<br />	A cerca de lo que necesita<br />	Hace cosas que dan miedo detrás de la 	fachada<b...
Ejemplo (CreditCard)<br />
CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.ch...
CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.ch...
CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardProcessor.init(…);<br />CreditCardcc = new Cr...
CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardProcessor.init(…);<br />CreditCardcc = new Cr...
CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />OfflineQueue.start();<br />CreditCardProcessor.init(…);...
CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />OfflineQueue.start();<br />CreditCardProcessor.init(…);...
CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />Database.connect(…);<br />OfflineQueue.start();<br />Cr...
CreditCard<br />¿Se ve un patrón?<br />
CreditCard<br />Miente sobre sus dependencias.<br />El orden de las inicializaciones no es explicito.<br />
CreditCard<br />¿Qué podemos hacer?<br />
CreditCard (Refactorizado)<br />@Test<br />publicvoidcreditCardChargeTest() {<br />Databasedb = Database(“connectionStr”);...
CreditCard<br />¡Tenemos opciones!<br />
CreditCard<br />Pero…<br />	¿Y si termino con 20 parámetros?<br />	Es porque los tenias… solo que…<br />
Accounting 101<br />classAccountView {<br />Useruser;<br />publicAccountView() {<br />user = RPCClient.getInstance().getUs...
Análisis<br />	Uso de Estados Globales<br />Incorpora las dependencias del “singleton”<br />Miente a cerca de las dependen...
Accounting 101<br />classAccountView {<br />Useruser;<br />publicAccountView(Useruser) {<br />user = user;<br />}<br />}<b...
¿Por qué?<br />Construcción de objetos<br />	Trabajo en el constructor<br />Estados globales<br />Violaciones de la Ley de...
La ley de Deméter<br />	Cada unidad debería tener conocimiento limitado sobre otras unidades: solo unidades “relacionadas ...
ServiceLocator<br />Aka. Context, aka. Registry… etc.<br />Mejor que un singleton<br />Al menos el problema está en un sol...
ServiceLocator<br />ClassHouse {<br />	//… <br />publicHouse(Locatorlocator) {<br />	//… Qué tengo que mockear???<br />	}<...
House<br />ClassHouse {<br />//…<br />Windowwindow;<br />Doordoor;<br />Roofroof;<br />publicHouse(Locatorlocator) {<br />...
House (Refactorizado)<br />classHouse {<br />//…<br />Windowwindow;<br />Doordoor;<br />Roofroof;<br />publicHouse(Window ...
ServiceLocator<br />¿Qué otro problema tiene?<br />	Mezcla responsabilidades.<br />	Buscar y encontrar.<br />	Creación<br ...
Making a Mockery…<br />classLoginPage {<br />RPCClientclient;<br />HttpRequestrequest;<br />publicLoginPage(RPCClientclien...
Análisis<br />Para testear se hace complicado<br />Las dependencias no son las declaradas.<br />Crear rpcclient, entrenarl...
Making a Mockery…<br />classLoginPage {<br />Authenticatorauthenticator;<br />String cookie;<br />publicLoginPage(Authenti...
Making a Mockery<br />Cosas a tener en cuenta.<br />Tiempo de vida de los objetos que se pasan.<br />	Ej. Cookie puede ser...
TDD: ¿Cómo escribir código testeable?
Upcoming SlideShare
Loading in …5
×

TDD: ¿Cómo escribir código testeable?

606 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
606
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
10
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

TDD: ¿Cómo escribir código testeable?

  1. 1. TDD: Cómo escribir código testeable.<br />
  2. 2. ¿Hacer tests es bueno?<br />
  3. 3. ¿Por qué no los hacemos?<br />
  4. 4. Razones<br />Válidas<br />¡No se!<br />Código antiguo<br />UI<br />No hacer pruebas<br />Inválidas<br />El diseño es malo<br />De eso se encarga QA<br />No coge los bugs<br />Demasiadas interfaces<br />Es lento<br />Aburido….<br />Es dificil de cambiar<br />
  5. 5. Hacer tests es una habilidad<br />
  6. 6. Entonces…<br />¿Cómo se escribe código difícil de probar?<br />
  7. 7. ¿Por qué?<br />Construcción de objetos<br /> Trabajo en el constructor<br /> Estados globales<br /> Violaciones de la Ley de Deméter<br />
  8. 8. Localización del “new”<br />Estímulo<br />API de Pruebas<br />Clase bajo prueba<br />Verificaciones<br />
  9. 9. Localización del “new”<br />Otra clase<br />Estímulo<br />Otra clase<br />API de Pruebas<br />Clase bajo prueba<br />Verificaciones<br />Otra clase<br />Instanciado dentro de la clase<br />Pasado por referencias<br />Estados globales<br />
  10. 10. Localización del “new”<br />Otra clase<br />Otra clase<br />Otra clase<br />Otra clase<br />Estímulo<br />Otra clase<br />API de Pruebas<br />Clase bajo prueba<br />Otra clase<br />Verificaciones<br />Otra clase<br />
  11. 11. Localización del “new”<br />Otra clase<br />Otra clase<br />Otra clase<br />Otra clase<br />Estímulo<br />Otra clase<br />API de Pruebas<br />Clase bajo prueba<br />Costura<br />Otra clase<br />Verificaciones<br />Otra clase<br />
  12. 12. Localización del “new”<br />Clase Falsa<br />Estímulo<br />Clase Falsa<br />API de Pruebas<br />Clase bajo prueba<br />Costura<br />Verificaciones<br />Clase Falsa<br />
  13. 13. Localización del “new”<br />Lógica de Negocio<br />Construcción del <br />Grafo de Objetos y <br />Búsqueda<br />
  14. 14. Localización del “new”<br />Lógica de Negocio<br />Construcción del <br />Grafo de Objetos y <br />Búsqueda<br />
  15. 15. Localización del “new”<br />Clase Falsa<br />Estímulo<br />Clase Falsa<br />API de Pruebas<br />Clase bajo prueba<br />Verificaciones<br />Clase Falsa<br />Instanciado dentro de la clase<br />Pasado por referencias<br />Estados globales<br />
  16. 16. Ejemplo (House)<br />
  17. 17. House<br />classHouse {<br />Kitchenkitchen = new Kitchen();<br />Bedroombedroom;<br />publicHouse() <br /> {<br />this.bedroom = new Bedroom();<br /> }<br />}<br />
  18. 18. House Test<br />classHouseTests {<br /> @Test<br />publicvoidimpossibleTestOnIsolation() {<br /> // Can’treplaceanything!!!<br />}<br />}<br />
  19. 19. Análisis<br /> Fácil de instanciar pero…<br /> No se puede remplazar nada.<br /> Puede ser costoso computacionalmente<br /> El diseño está acoplado. <br /> Cerrado para extensión.<br />
  20. 20. House (Refactoizado)<br />classHouse {<br />Kitchenkitchen;<br />Bedroombedroom;<br />@Inject<br />publicHouse(Kitchenkitchen, Bedroombr) {<br />this.bedroom = br;<br />this.kitchen = kitchen;<br />}<br />}<br />
  21. 21. House Test (Refactorizado)<br />classHouseTests {<br /> @Test<br />publicvoideasierToTestOnIsolation() {<br />DummyKitchendk = new DummyKitchen();<br />DummyBedroomdb = new DummyBedroom();<br />House h = new House(dk,db);<br /> // …<br />}<br />}<br />
  22. 22. Ejemplo (Gardener)<br />
  23. 23. Gardener<br />class Garden {<br />Gardenerjoe;<br />public Garden(Gardenerjoe) {<br />joe.setWorkHours(new TwelveHours());<br />joe.setBoots(new ExpensiveBoots());<br /> this.joe = joe;<br /> }<br />}<br />
  24. 24. Análisis<br /> Mejor pero… <br /> No se pueden remplazar las botas o el horario.<br /> La prueba puede tardar (horario de 12h).<br /> Necesita el jardinero pero se encarga de configurarlo.<br />
  25. 25. Gardener (Refactored)<br />class Garden {<br />Gardenerjoe;<br /> @Inject<br />public Garden(Gardenerjoe) {<br /> this.joe = joe;<br /> }<br />}<br />classGardenerProvider {<br /> @Provides<br />GardenergetGardener(ExpensiveBootsboots, TwelveHoursschedule) {<br />Gardenerjoe = new Gardener();<br />joe.setWorkHours(schedule);<br />joe.setBoots(boots);<br />returnjoe;<br /> }<br />}<br />
  26. 26. ¿Por qué?<br />Construcción de objetos<br />Trabajo en el constructor<br /> Estados globales<br /> Violaciones de la Ley de Deméter<br />
  27. 27. Costo de la construcción<br /> Para testear, primero hay que instanciar pero…<br />El trabajo dentro del constructor no tiene “costuras”<br /> No se puede sobre escribir.<br /> La prueba tiene que saber navegar lo que haya dentro <br />
  28. 28. Ejemplo (Super Car)<br />
  29. 29. Super Car<br />class Car {<br />Engineengine;<br />public Car(Filefile) {<br />String m = readModelFromCfg(file);<br />engine = new EngineFactory().create(m);<br />}<br />}<br />
  30. 30. Super Car Test<br />classCarTest {<br />@Test<br />publicvoidhardToSetupTest() {<br />Filefile = new File(“custom.conf”);<br /> Car car = new Car(file);<br /> //Asserts...<br />}<br />}<br />
  31. 31. Análisis<br /> Pasamos un File cuando lo que se necesita es un Engine.<br /> Toda prueba que necesite Car requiere eta parafernaria.<br />Accede al sistema de ficheros<br /> Lento.<br /> No es una prueba unitaria realmente.<br />
  32. 32. Super Car (Refactorizado)<br />class Car {<br />Engineengine;<br />@Inject<br />public Car(Engineengine) {<br />this.engine = engine;<br />}<br />}<br />
  33. 33. Super Car (Provider)<br />classCarEngineProvider {<br />@Provides<br />EnginegetEngine(EngineFactoryfactory, @EngineModelStringmodel) {<br />returnfactory.create(model);<br />}<br />}<br />
  34. 34. Super Car Test (Refactorizado)<br />classCarTest {<br />@Test<br />publicvoideasyToSetupTest() {<br />Engineengine = new FakeEngine();<br /> Car car = new Car(engine);<br /> //Asserts...<br />}<br />}<br />
  35. 35. ¿Por qué?<br />Construcción de objetos<br /> Trabajo en el constructor<br /> Estados globales<br /> Violaciones de la Ley de Deméter<br />
  36. 36. Locura<br />
  37. 37. Locura<br />Definición:<br />Repetir una misma acción una y otra vez y esperar obtener un resultado diferente.<br />Albert Einstein<br />
  38. 38. O mejor dicho… Estados Globales<br />class X {<br />public X() {<br />}<br />publicintdoSomething() {<br /> //… Something…<br />}<br />}<br />
  39. 39. Estados Globales<br />int a = new X().doSomething();<br />int b = new X().doSomething();<br />
  40. 40. Estados Globales<br />¿ a == b ó a != b ?<br />
  41. 41. Estados Globales<br />X x1 = new X();<br />Y<br />Q<br />X<br />Z<br />
  42. 42. Estados Globales<br />X x1 = new X();<br />X x2 = new X();<br />Y<br />Q<br />X<br />Z<br />Y<br />Q<br />X<br />Z<br />
  43. 43. Estados Globales<br />X x1 = new X();<br />x1.doSomething();<br />A == B ó A != B<br />X x2 = new X();<br />x2.doSomething();<br />Y<br />Q<br />X<br />Z<br />GS<br />Y<br />Q<br />X<br />Z<br />
  44. 44. Consecuencias<br /> Múltiples ejecuciones<br /> Resultados inesperados<br /> Orden importa<br /> No se pueden ejecutar en paralelo<br /> Localización del estado desacotado.<br />
  45. 45. Estados Globales Ocultos<br />System.currentTime()<br />new Date()<br />Math.random()<br />
  46. 46. Consecuencias<br /> La API miente:<br /> A cerca de lo que necesita<br /> Hace cosas que dan miedo detrás de la fachada<br />
  47. 47. Ejemplo (CreditCard)<br />
  48. 48. CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.charge(200);<br />}<br />
  49. 49. CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.charge(200);<br />}<br />java.lang.NullPointerException at talk2.CreditCard.charge(CreditCard.java:48)<br />
  50. 50. CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardProcessor.init(…);<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.charge(200);<br />}<br />
  51. 51. CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />CreditCardProcessor.init(…);<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.charge(200);<br />}<br />java.lang.NullPointerException at talk2.CreditCardProcessor.init(CreditCardProcessor.java:134)<br />
  52. 52. CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />OfflineQueue.start();<br />CreditCardProcessor.init(…);<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.charge(200);<br />}<br />
  53. 53. CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />OfflineQueue.start();<br />CreditCardProcessor.init(…);<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.charge(200);<br />}<br />java.lang.NullPointerException at talk2.OffileQueue.start (OfflineQueue.java:203)<br />
  54. 54. CreditCard<br />@Test<br />publicvoidcreditCardChargeTest() {<br />Database.connect(…);<br />OfflineQueue.start();<br />CreditCardProcessor.init(…);<br />CreditCardcc = new CreditCard(“1234567890”);<br />cc.charge(200);<br />}<br />
  55. 55. CreditCard<br />¿Se ve un patrón?<br />
  56. 56. CreditCard<br />Miente sobre sus dependencias.<br />El orden de las inicializaciones no es explicito.<br />
  57. 57. CreditCard<br />¿Qué podemos hacer?<br />
  58. 58. CreditCard (Refactorizado)<br />@Test<br />publicvoidcreditCardChargeTest() {<br />Databasedb = Database(“connectionStr”);<br />OfflineQueuequeue = new OfflineQueue(db);<br />CreditCardProcessorccProcessor = new CreditCardProcessor(queue);<br />CreditCardcc = new CreditCard(“1234567890”, ccProcesor);<br />cc.charge(200);<br />}<br />
  59. 59. CreditCard<br />¡Tenemos opciones!<br />
  60. 60. CreditCard<br />Pero…<br /> ¿Y si termino con 20 parámetros?<br /> Es porque los tenias… solo que…<br />
  61. 61.
  62. 62. Accounting 101<br />classAccountView {<br />Useruser;<br />publicAccountView() {<br />user = RPCClient.getInstance().getUser();<br />}<br />}<br />
  63. 63. Análisis<br /> Uso de Estados Globales<br />Incorpora las dependencias del “singleton”<br />Miente a cerca de las dependencias.<br />
  64. 64. Accounting 101<br />classAccountView {<br />Useruser;<br />publicAccountView(Useruser) {<br />user = user;<br />}<br />}<br />
  65. 65. ¿Por qué?<br />Construcción de objetos<br /> Trabajo en el constructor<br />Estados globales<br />Violaciones de la Ley de Deméter<br />
  66. 66.
  67. 67. La ley de Deméter<br /> Cada unidad debería tener conocimiento limitado sobre otras unidades: solo unidades “relacionadas de cerca” a la unidad actual. <br /> Cada unidad debe de hablar solamente con sus amigos; No hablar con extraños.<br /> Habla solamente con tus amigos inmediatos.<br />
  68. 68. ServiceLocator<br />Aka. Context, aka. Registry… etc.<br />Mejor que un singleton<br />Al menos el problema está en un solo lugar.<br />Es testeable… pero no muy bonito.<br />Esconde las dependencias reales.<br />Las dependencias hacen el código poco reusable.<br />
  69. 69. ServiceLocator<br />ClassHouse {<br /> //… <br />publicHouse(Locatorlocator) {<br /> //… Qué tengo que mockear???<br /> }<br />}<br />
  70. 70. House<br />ClassHouse {<br />//…<br />Windowwindow;<br />Doordoor;<br />Roofroof;<br />publicHouse(Locatorlocator) {<br />this.window = locator.getWindow();<br />this.door = locator.getDoor();<br />this.roof = locator.getRoof();<br /> }<br />}<br />
  71. 71. House (Refactorizado)<br />classHouse {<br />//…<br />Windowwindow;<br />Doordoor;<br />Roofroof;<br />publicHouse(Window w, Door d, Roof, r) {<br />this.window = w;<br />this.door = d;<br />this.roof = r;<br /> }<br />}<br />
  72. 72. ServiceLocator<br />¿Qué otro problema tiene?<br /> Mezcla responsabilidades.<br /> Buscar y encontrar.<br /> Creación<br />Se necesita tener una interface para testear<br />Si dependes de ServiceLocator, dependes de todo lo demás.<br />
  73. 73. Making a Mockery…<br />classLoginPage {<br />RPCClientclient;<br />HttpRequestrequest;<br />publicLoginPage(RPCClientclient, HttpRequestrequest) {<br />this.client = client;<br />this.request = request;<br /> }<br />publicbooleanlogin() {<br />String cookie = request.getCookie();<br />returnclient.getAuthenticator().authenticate(cookie);<br /> }<br />}<br />
  74. 74. Análisis<br />Para testear se hace complicado<br />Las dependencias no son las declaradas.<br />Crear rpcclient, entrenarlo para devolver un authenticator, crear un authenticator… Etc.<br />
  75. 75. Making a Mockery…<br />classLoginPage {<br />Authenticatorauthenticator;<br />String cookie;<br />publicLoginPage(Authenticatorauth, @Cookie String cookie) {<br />this.cookie= cookie;<br />this.authenticator = auth;<br /> }<br />publicbooleanlogin() {<br />returnauthenticator.authenticate(cookie);<br /> }<br />}<br />
  76. 76. Making a Mockery<br />Cosas a tener en cuenta.<br />Tiempo de vida de los objetos que se pasan.<br /> Ej. Cookie puede ser distinto para cada vez<br />

×