Testing y Mocking: Probando Aplicaciones · Testing y Mocking: Probando Aplicaciones. Introducción...

Post on 03-Jul-2020

18 views 0 download

Transcript of Testing y Mocking: Probando Aplicaciones · Testing y Mocking: Probando Aplicaciones. Introducción...

Testing y Mocking: Probando Aplicaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

1 Introducción

2 Tipos de tests

3 Inyección de dependencias, DI

4 Mocking

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

1 Introducción

2 Tipos de tests

3 Inyección de dependencias, DI

4 Mocking

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

1 IntroducciónPlanteando el problema¿Qué es testing?

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Cuando escribimos software

falta tiempohacemos suposicionesel código queda feoproyectos legacy sin andarfalta tiempo de refactor

Por todo esto la calidad se degrada

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Si bien no hay silver bullets

Veremos como, testeando, el proyecto sale mejor. Los tests:

generan confianzapermiten la detección temprana de bugsayudan a detectar inconsistencias en RQsmejoran la productividad (a largo plazo)

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Si bien no hay silver bullets

Veremos como, testeando, el proyecto sale mejor.Siempre considerando que, los tests . . .

no garantizan el éxito de un proyectono aseguran 100% de calidadno son aplicables a todo proyectoobligan a pensar antes de programar! a

aQueda como ejercicio determinar si esto es bueno o no

Pero, ¿Qué es testing?

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

1 IntroducciónPlanteando el problema¿Qué es testing?

Introducción Tipos de tests Inyección de dependencias, DI Mocking

¿Qué es testing?

Testear el software es:validar su comportamientovalidar un set de datosvalidar requerimientosuna forma de medir la calidad

Testear el software no es:asegurar el éxito del proyectoafirmar que no hay bugs"a silver bullet"

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Algunos "extras"

El testing (o, mejor, TDD) ayuda a:

especificar requerimientosespecificar comportamientoevitar regresionesrefactorizar

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Cómo testear

Vimos muchos motivos, muy dispares . . .¿todo eso con un test?

No, hay distintos tipos. Veamos algunos.

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

1 Introducción

2 Tipos de tests

3 Inyección de dependencias, DI

4 Mocking

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

2 Tipos de testsTests unitariosTests de regresiónIntegración ContinuaTests de integraciónStress testsOtras clasificaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Tests unitarios: el tipo de test mas básicoPrueba sólo una clase (aislada)repetibles y deterministicosautomáticosse componen de varios tests casescada test case prueba un aspecto de funcionalidad . . .que es un requerimiento (de la clase)

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Veamos un ejemplo. . .

1 def Vehiculo.avanzar2 if combustible > 0 then3 combustible=combustible-14 return self.velocidad5 else6 destruido = true7 return 08 end9 end

10 def Avion.velocidad() return 20 end11 def Submarino.velocidad() return 10 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Para el ejemplo

Armemos un test unitario que verifique que losvehículos se destruyen si no tienen combustible

1 def test_destruccion2 avion = Avion.new3 submarino = Submarino.new4

5 {avion, submarino}.6 each.set_combustible 07

8 Expect{ avion.avanzar == 0 }9 Expect{ avion.destruido == true }

10 Expect{ submarino.avanzar == 0 }11 Expect{ submarino.destruido == true }12 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

2 Tipos de testsTests unitariosTests de regresiónIntegración ContinuaTests de integraciónStress testsOtras clasificaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Tests de regresión

Los tests de regresión en verdad son tests unitarios

que ya funcionanademás de requerimientos puede validar "bugs"se corren luego de cada

refactorbugfixhacer un deployen general, modificación

verifican si existe una regresión

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Siguiendo el ejemplo. . .

1 def test_destruccion2 avion = Avion.new3 submarino = Submarino.new4 jeep = Jeep.new5

6 {avion, submarino, jeep}.7 each.set_combustible 08

9 Expect{ avion.avanzar == 0 }10 Expect{ avion.destruido == true }11 Expect{ submarino.avanzar == 0 }12 Expect{ submarino.destruido == true }13 Expect{ jeep.avanzar == 0 }14 Expect{ jeep.destruido == false }15 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Y corremos el test...

¿Qué falló?

1 def Vehiculo.avanzar2 if combustible > 0 then3 combustible=combustible-14 return self.velocidad5 else6 destruido = true7 return 08 end9 end

10 def Avion.velocidad() return 20 end11 def Submarino.velocidad() return 10 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Corregimos el error, y ahora si . . .

1 def Jeep.avanzar2 if combustible > 0 then3 combustible=combustible-14 return self.velocidad5 else6 return 07 end8 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

2 Tipos de testsTests unitariosTests de regresiónIntegración ContinuaTests de integraciónStress testsOtras clasificaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Integración continua

Si un test que antes funcionaba ahora falla:

existe una regresiónentre el último commit testeado y el actualmenos commits =⇒ mas fácildescubrir la causauno por commit se llama integración continua!

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Integración continua

Un servidor de ICrequiere tests "rápidos"puede schedulear tests "lentos" diariamenteprovee feedback del commit en cuestión de minutosrequiere tests deterministicos y repetibles!

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Integración continua

Un servidor de ICrequiere tests "rápidos"puede schedulear tests "lentos" diariamenteprovee feedback del commit en cuestión de minutosrequiere tests deterministicos y repetibles!

Veamos una demo con el Cruise Control!

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

2 Tipos de testsTests unitariosTests de regresiónIntegración ContinuaTests de integraciónStress testsOtras clasificaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Tests de integración

Tests de Integración != Integración Continua

Los tests de integración:prueban mas de un componentepueden implementarse como tests unitariosno siempre son deterministicos o repetiblesno siempre son "rápidos"

Introducción Tipos de tests Inyección de dependencias, DI Mocking

¿Por qué?

Tener solo tests de integración no es cost-effective:

1 def foo(x, y, z)2 if z then3 bar(x)4 else5 baz(y)6 end7 end

¿Cuántos tests unitarios son?¿Cuántos tests de integración son?

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Tests de integración

Integración

N Función x y z1 foo true true true2 foo true true false3 foo true false true4 foo true false false5 foo false true true6 foo false true false7 foo false false true8 foo false false false

Unitario

N Función x y1 bar true N/A2 bar false N/A3 baz N/A true4 baz N/A false

Hacen falta 8 tests de integración y 4 unitariosEntonces, ¿conviene usar tests de integración?

Introducción Tipos de tests Inyección de dependencias, DI Mocking

¿Por qué no?

Los tests para ambas funciones son simplesPero en conjunto andan muy mal!

test_multiplicar_manzanas ()

test_get_peras ()

multiplicar_manzanas( get_peras () )

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

2 Tipos de testsTests unitariosTests de regresiónIntegración ContinuaTests de integraciónStress testsOtras clasificaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

stress tests

¿qué son?verifican el funcionamiento bajo cargasimulan un entorno de producciónpocas veces son automáticos

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

2 Tipos de testsTests unitariosTests de regresiónIntegración ContinuaTests de integraciónStress testsOtras clasificaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Orientado a los datos

Cuando armarmos un test:

Se toma un móduloDeterminamos las entradasEvaluamos las salidasBuscamos casos límiteExiste un contrato implicito

1 def test_division2 Expect{1 == 2/2}3 Expect{Err == 1/0}4 Expect{-5 == -5/1}5 Expect{-5 == 5/-1}6 Expect{5 == -5/-1}7 # Etc ...8 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Orientado al comportamiento

Cuando armarmos un test (en un proyecto testeable):

Pensamos en término de objetos y relaciones. . . y no tanto en función de la entrada-salidaLos objetos tienen interfaces. . . e interactuan con otros objectosExiste un contrato explícitoNo es necesario evaluar tantos casos limite

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

1 Introducción

2 Tipos de tests

3 Inyección de dependencias, DI

4 Mocking

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

3 Inyección de dependencias, DIQué es DIPara qué usar DIDI aplicado

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Empecemos con un ejemplo

¿Qué pasa cuando corremos el test?

1 def test_compra2 item = Empanada.new3 item.precio = 24 venta = Venta.new5 venta.add item6 Expect{ venta.comprar == true }7 Expect{ venta.total == 2 }8 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Empecemos con un ejemplo

¿Qué pasa cuando corremos el test?

1 def test_compra2 item = Empanada.new3 item.precio = 24 venta = Venta.new5 venta.add item6 Expect{ venta.comprar == true }7 Expect{ venta.total == 2 }8 end

Null ptr exception

Introducción Tipos de tests Inyección de dependencias, DI Mocking

¿Qué hay en la clase Venta?

1 def Venta.new2 db = ResourceLocator.DbSingleton.get_instance3 cliente = db.get_instance.get_ultimo_cliente4 end5

6 def Venta.add item7 raise "Te falta plata!"8 if cliente.billetera.saldo < self.total9

10 items.add item11 end12

13 def Venta.comprar14 cliente.billetera.debitar self.total15 return self.total16 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Arreglemos el test. . .

1 def test_compra2 # Scaffolding3 dagoberto = Cliente.new4 dagoberto.billetera = Billetera.new5 dagoberto.billetera.saldo = 26

7 singleton = Singleton.new8 singleton.instance = DB_Factory.new9 ResourceLocator.DbSingleton = singleton

10 db = ResourceLocator.DbSingleton.get_instance11 db.set_cliente = dagoberto12

13 item = Empanada.new14 item.precio = 215

16 # Test real - Dependencias ocultas17 venta = Venta.new18 venta.add item19 Expect{ venta.comprar == true }20 Expect{ venta.total == 2 }21 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Arreglemos el test. . .

En el test se reflejan varios problemas

Hay mucho "scaffolding"El sentido del test se pierdeHay dependencias ocultasSe accede a mas objetos que losnecesariosEl scope demasiado grande

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

3 Inyección de dependencias, DIQué es DIPara qué usar DIDI aplicado

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Podemos mejorar el diseño con DI

La inyección de dependencias

separa la construcción del usohace explicitas las dependenciasayuda a simplificar la lógicalimita el scope de un objetoreduce el factor sorpresa!

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Dependency Injection Demystified

La inyección de dependencias es en realidad muy simple

Ask for what you need

A un objeto se le dan sus dependenciasEl objeto inyectado no crea sus propias dependenciasNo mas singletons ni ServiceLocatorsChau estado global

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

3 Inyección de dependencias, DIQué es DIPara qué usar DIDI aplicado

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Resultados

1 def Venta.new cuenta2 self.cuenta = cuenta3 end4

5 def Venta.add item6 raise "Te falta plata!"7 if cuenta.saldo < self.total8

9 items.add item10 end11

12 def Venta.comprar13 cuenta.debitar self.total14 return self.total15 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Resultados

No solo el código es mas prolijo, el test también

1 def test_compra2 item = Empanada.new3 item.precio = 24 cuenta = Cuenta.new5 cuenta.saldo = 26

7 # Test real - la dependencia es clara8 venta = Venta.new cuenta9 venta.add item

10 Expect{ venta.comprar == true }11 Expect{ venta.total == 2 }12 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

1 Introducción

2 Tipos de tests

3 Inyección de dependencias, DI

4 Mocking

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Mocking: seguimos el ejemplo

Sigamos el ejemplo de DI. ¿Podemos mejoralo mas?¿Qué estamos testeando?¿Queremos testear el objeto "Cuenta"?¿Y si Cuenta aún no está implementado?¿O tiene muchos muchos bugs?¿O es muy lento? (P. ej: accede a la BD)

Mocking es la solución!

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Mocking

¿Pero qué es "mocking"?

técnica para testearun mock es un objeto "dummy"son "fakes" de partes del sistemadefinen su comportamiento "on the fly"usan la interfaz de un objeto real

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

4 MockingHerencia, polimorfismo y MockingDI y MockingEjemplos

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Cómo funciona

Se basa en el polimorfismoEl Objeto testeable usa un objeto "mockeable"El mock hereda de una interfaz común al objeto realAl objeto testeable no le "importa" cual de los dos usa

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Cómo funciona

El objeto testeado no sabe si es una implementación realTampoco importa, el mock usa la interfaz conocidaEs un concepto similar al de usar varias implementaciones

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

4 MockingHerencia, polimorfismo y MockingDI y MockingEjemplos

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Por qué DI + Mocking

DI obliga a separar componentesPara testear hace falta aislar módulosAislar módulo =⇒ sacar dependenciasMocking permite simular otros módulosErgo . . .

Mocking + DI r0ck$!!!!!11uno1

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Outline

4 MockingHerencia, polimorfismo y MockingDI y MockingEjemplos

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Mocking: seguimos el ejemplo II

Veamos como queda el ejemplo

1 def test_compra2 item = MockItem.new3 cuenta = MockCuenta.new4 item.expect_call("precio").returns( 2 )5 cuenta.expect_call("saldo").returns( 2 )6 cuenta.expect_call("debitar", 2).returns( true )7

8 venta = Venta.new cuenta9 venta.add item

10 Expect{ venta.comprar == true }11 Expect{ venta.total == 2 }12 end

Introducción Tipos de tests Inyección de dependencias, DI Mocking

Mocking: ejemplo explicado

Algunos detalles del ejemplo:

1 item.expect_call("precio").returns( 2 )2 cuenta.expect_call("saldo").returns( 2 )3 cuenta.expect_call("debitar", 2).returns( true )

El comportamiento se define con el mismo testSe define un set de "expectations"Las expectations son tests

cada una debe cumplirseverifican el comportamiento del objeto

Usando DI podemos inyectar una cuenta falsa. . . sería posible de no usar DI?

¿Preguntas?

42

¿Preguntas?