SlideShare una empresa de Scribd logo
1 de 54
Descargar para leer sin conexión
Doble o Nada
Jesús Miguel Benito Calzada - PHP Cáceres
Jesús Miguel Benito Calzada
Software developer @ Intexdev
beni0888@hotmail.com
@beni0888
https://github.com/beni0888
¿Quién soy?
¿De qué va esto?
❖ Unit testing
❖ PHPUnit
❖ Test Doubles
❖ Mockery
DISCLAIMER
❖ Esta es mi manera de hacer las cosas,
seguro que las hay mejores.

❖ No soy un experto en testing, pero a
veces hago tests ;).

❖ Esto no trata de TDD, aunque es posible
que el término aparezca en más de una
ocasión.
Unit Test
Herramienta que nos permite comprobar el correcto funcionamiento
de una unidad de código. Esto sirve para asegurar que cada una
de las partes del sistema funciona correctamente por separado.
Debe ser:
❖ Atómico
❖ Independiente
❖ Inocuo
❖ Rápido
Atómico
❖ El test prueba la mínima cantidad de funcionalidad
posible .
❖ Un solo comportamiento del método o función.
❖ Distintos caminos de ejecución => un test para
cada camino
Independiente
❖ El resultado de la ejecución de un test no debe
depender de otros.
❖ No debe ser parte de una secuencia de tests que
deba ejecutarse en un orden determinado
Inocuo
❖ La ejecución de un test no debe alterar el estado del
sistema (ficheros, base de datos, etc) => no
mancha :)
❖ El resultado debe ser el mismo ya lo ejecutemos una
o mil veces => idempotente
Rápido
❖ Normalmente lanzaremos un gran número de tests
en cada ejecución
❖ Tener que esperar resulta improductivo y aburrido
=> no haríamos test (menos de los que ya hacemos
:P)
❖ Un test unitario debería ejecutarse en una pequeña
fracción de segundo
Características ATRIP
❖ Automatic: pueden ser corridos mediante un simple comando.
❖ Thorough (minucioso): cuanto más testemos mejor.
❖ Repeatable: podemos ejecutarlo una y mil veces con el
mismo resultado.
❖ Independant: el resultado de un test no debe depender de
otros.
❖ Professional: debemos ser igual de exigentes al escribir los
tests de lo que lo somos al escribir nuestra lógica de negocio
(prohibidas ñapas en los tests)
Un poco de terminología
Un poco de terminología
❖ SUT: Subject/System Under Test.
❖ Colaboradores: objetos que interactúan con el
SUT y que necesita para implementar su
funcionalidad.
❖ Fixtures: Algo así como los elementos de contexto,
datos u objetos que necesitamos para construir el
escenario que requiere el test.
Anatomía de un unit test:
AAA
❖ Arrange: configuración y/o carga de fixtures,
instanciar colaboradores, etc.
❖ Act: ejercitar la funcionalidad que queremos
testear.
❖ Assert: afirmar que el estado es el esperado
PHPUnit
❖ Versión para php de los frameworks xUnit
❖ Creado por Sebastian Bergman
❖ Estándar de facto en php
❖ https://phpunit.de/
Instalación
PHP Archive (PHAR):
Composer:
TestCase
❖ Los tests se organiza en clases que heredan de la clase
PHPUnit_Framework_TestCase
❖ Cada unit test es un método de la clase
❖ Para que un método sea considerado un test, su nombre debe llevar el
sufijo test o debe estar marcado con la anotación @test
setUp() y tearDown()
❖ setUp(): método que se ejecuta antes de cada test,
pude servirnos para la fase de arrangement.
❖ tearDown(): método que se ejecuta después de
cada test, puede utilizarse para labores de limpieza
Aserciones
Excepciones
❖ Podemos hacer que el test falle si no se lanza una
excepción
Organización de los tests
❖ Fichero de configuración:
❖ Sistema de archivos:
• phpunit.xml.dist

• phpunit.xml
Mocks
❖ Son objetos que imitan a otros objetos de nuestro sistema y
que utilizamos en su lugar a la hora de hacer tests => “Test
Doubles”
❖ Suelen reemplazar a los colaboradores en los tests unitarios
❖ En ocasiones pueden incluso reemplazar al SUT!!
❖ Permiten aislar la funcionalidad del SUT de la de los
colaboradores
❖ Facilitan la configuración del escenario del test
❖ Permiten que los tests unitarios sean rápidos
¿Todos los Test Doubles
son Mocks?
¿Todos los Test Doubles
son Mocks?
❖ No
❖ Mock es una palabra empleada en el lenguaje
informal para referirse a los test doubles en
general.
❖ Existen varios tipos específicos de test doubles.
❖ Los mocks son un tipo específico de test double
❖ Vamos a verlos!
Dummies
❖ Es un test doble que sólo se usa para rellenar
parámetros de un constructor o método

❖ Nunca es usado dentro del la funcionalidad del SUT
que vamos a ejercitar (al menos en el camino de
ejecución que vamos a testear)

❖ En teoría sus métodos deben devolver NULL (si lo
generamos a mano implementando una interface)

❖ Generalmente implementarán una interface
Dummies
Algunos ejemplos robados de http://php-and-symfony.matthiasnoback.nl/2014/07/test-doubles/
❖ Dummies con phpunit:
❖ Ejemplo con mockery:
Stubs
❖ Son test doubles que devuelve un valor fijo
predeterminado en las llamadas a sus métodos
❖ No tiene en cuenta los argumentos pasados a los
métodos (no se verifican)
❖ No tiene en cuenta el número de llamadas
realizadas a los métodos
Stubs
Fakes
❖ Son una especie de stubs con cierta lógica distinta de la
verdadera lógica del colaborador al que imita y normalmente
más ligera
❖ Suelen emplearse cuando el SUT depende de un colaborador
que no está disponible, es lento o simplemente dificulta el test
❖ Tampoco tiene en cuenta el número de llamadas a los métodos
❖ No realiza verificación de parámetros
❖ Pueden volverse tan complicados que requieran sus propios
tests unitarios
Fakes
Spies
❖ Es una especie de fake o stub que que recaba
información en las llamadas realizadas a sus
métodos y permite inspeccionarla después
❖ Puede por ejemplo registrar qué métodos han sido
llamados, número de veces, parámetros recibidos,
etc.
Spies
Mocks
❖ Son un tipo muy especial de test double
❖ Se parece a los spies en el sentido de que registran
las llamadas realizadas y los parámetros pasados
❖ Es mucho más potente que un spy
❖ Permite algo que el resto no: validación del
comportamiento o interacción
Mocks
❖Permite definir un conjunto de expectativas para validar:
• Llamadas a los métodos
• Parámetros pasados: número, tipo y valor
• Número de veces llamado: exacto, mínimo, máximo
• Orden de las llamadas
❖Pueden actuar como stubs devolviendo valores
❖Existen un gran número de librerías que permiten generar
mocks de objetos (o interfaces) al vuelo, en php: phpunit,
mockery, prophecy, fake, etc.
Mocks
To mock or not to mock?
❖ Los mocks son una herramienta muy potente y su uso tiene sus pros y sus
contras
❖ Su uso puede llevar a sobrespecificación y dar lugar a tests frágiles
❖ El uso o no de los mismos permite diferenciar dos tipos de validación:
• Validación del estado: propiedades de objetos, valores devueltos, etc.
• Validación del comportamiento o interacción: llamadas a métodos,
número de veces, orden, argumentos, etc.
❖ Esto a su vez da lugar a dos estilos de TDD:
• Classicist TDD (Chicago School): basado en la validación del estado
• Mockist TDD (London School): basado en la validación del
comportamiento
Classic TDD vs Mockist TDD
❖ TDD con mocks permite seguir un diseño outside-in:
• Se empieza creando un test para la capa más externa del sistema y se
mockean lo colaboradores
• Posteriormente se desarrollan tests para los colaboradores
• Así sucesivamente se van descubriendo los objetos del sistema y su api
❖ TDD clásico suele seguir un diseño middle-out:
• Se elige una característica del dominio por la que comenzar la
implementación
• Se avanza desarrollando el resto de objetos del dominio necesarios para
el funcionamiento de dicha característica
• Una vez desarrollada se construye la capa superior
Classic TDD vs Mockist TDD
❖ El uso de mocks suele hace que la fase de preparación del tests sea
más costosa ya que hay que definir los mocks y sus expectativas
❖ En el TDD clásico se suele reutilizar el código de configuración
entre varios tests, este suele ir el método setUp
❖ Un fallo en un objeto de un sistema con mocks causará fallos sólo en
los tests del objeto
❖ Un fallo en un objeto de un sistema clásico puede provocar fallos en
los tests de todos los objetos que tienen al objeto como colaborador
=> puede ser complicado localizar el fallo
❖ Hacer tests de granularidad fina minimizará este problema
❖ Como ventaja los tests clásicos comprueban la integración real de
los objetos del sistema
Classic TDD vs Mockist TDD
❖ Los tests con mocks están muy acoplados a la
implementación del SUT => cambios en la
implementación provocarán ruptura de los tests
❖ Los tests clásicos sólo se preocupan del estado,
da igual cual sea la implementación si el
resultado es el esperado
❖ La refactorización del código testado con mocks
conllevará un trabajo importante de refactorización
de los tests
Mockery
❖ Librería php que nos permite crear mocks al vuelo
❖ https://github.com/padraic/mockery
❖ http://docs.mockery.io/en/latest/
❖ Instalación:
$ composer require mockery/mockery
Integración con phpunit
public function tearDown() {
Mockery::close();
}
<listeners>
<listener class="MockeryAdapterPhpunitTestListener"></listener>
</listeners>
❖ En el fichero de configuración:
❖ En cada test case:
❖ Si no hacemos esto no se validarán las expectativas
Mockeando
$mock = Mockery::mock(‘foo');

// Mock llamado “foo”
$mock = Mockery::mock(array('foo'=>1,'bar'=>2));

// Mock sin nombre con array de espectativas
$mock = Mockery::mock('foo', array('foo'=>1,'bar'=>2));

// Mock “foo” con espectativas
$mock = Mockery::mock('stdClass');

// Mockeando clases
$mock = Mockery::mock(‘PSRLogLoggerInterface’);

// Mockeando interfaces
$mock = Mockery::mock('stdClass, MyInterface1, MyInterface2');

// Mockeando una clase e implementando interfaces
Definiendo espectativas
• shouldReceive(method_name)

// El método debe ser llamado
• shouldReceive(method1, method2, ...)

//Varios métodos
• shouldReceive(array('method1'=>1, 'method2'=>2, …))

// Los métodos deben ser llamados y devolverán estos valores
• with(arg1, arg2, ...) / withArgs(array(arg1, arg2, ...))

// validando los parámetros recibidos
• withAnyArgs(), withNoArgs() //…
• andReturn(value) / andReturn(value1, value2, ...)

// estableciendo valores devueltos en la llamada / llamadas
• andReturnNull() / andReturn([NULL]) / andReturnValues(array) /
andReturnUsing(closure, ...)
Definiendo espectativas
• andThrow(Exception) / andThrow(exception_name, message)

// lanzando excepciones en la llamada
• andSet(name, value1) / set(name, value1)

// establecer valor de atributos en la llamada
• passthru() // llama a la verdadera implementación del método
• zeroOrMoreTimes() / once() / twice() / times(n) / never() /
atLeast() / atMost() / between(min, max)

// estableciendo el número de llamadas: exacto, mínimo, máximo, entre
• ordered() / ordered($group)

// define el orden de llamadas o el orden dentro de un grupo
• globally() // el orden se entre todos los mocks, no sólo el actual
• byDefault() // define expectativas por defecto, que puden ser
sobreescritas en los tests concretos
Validando argumentos
• with(‘some-value’) // con un valor concreto
• with(Mockery::any()) OR with(anything()) // cualquier valor
• with(Mockery::type('resource')) OR with(resourceValue()) OR
with(typeOf(‘resource')) // con un tipo concreto
• with(Mockery::on(closure)) // validando mediante un closure
• with('/^foo/') OR with(matchesPattern(‘/^foo/')) // regexp
• with(Mockery::ducktype('foo', ‘bar')) //cualquier objeto que
contenga los métodos indicados
• with(Mockery::mustBe(2)) OR with(identicalTo(2)) // ===
• with(Mockery::not(2)) OR with(not(2)) // negando
• with(Mockery::anyOf(1, 2)) OR with(anyOf(1,2)) // varias opciones
Validando argumentos
• with(Mockery::notAnyOf(1, 2)); // que no esté entre las opciones
• with(Mockery::subset(array(0 => ‘foo’))); 

// un array que contenga este subconjunto
• with(Mockery::contains(value1, value2)); 

// un array que contenga estos valores
• with(Mockery::hasKey(key)); 

// un array con una clave
• with(Mockery::hasValue(value));

// con un valor
Partial Mocks
• $mock = Mockery::mock(‘MyClass[foo, bar]’);
Método tradicional, mockea los métodos foo y bar de la clase,

hay que definir su comportamiento mediante expectativas.
• $mock = Mockery::mock(‘MyClass')->makePartial();
Método pasivo, las llamadas son enviadas a los métodos originales,

excepto si se han definido expectativas para el método.
• $mock = Mockery::mock(new MyClass);
Mock parcial con proxy, crea un proxy que intercepta las llamadas en 

lugar de heredar del objeto a mockear, esto permite mockear clases 

declaradas “final” o con métodos “final”. El inconveniente es que no
validará los type hints ya que no extiende la clase a mockear.
❖ Permiten mockear sólo algunos métodos de los objetos
Y más en la documentación…
❖ Mockear atributos públicos

❖ Métodos estáticos

❖ Conservar el paso de parámetros por refencia

❖ Mockear cadenas de Demeter

❖ Grabación de interacciones con objetos reales

❖ …
Ejemplos Reales
Referencias
❖ http://blog.8thlight.com/uncle-bob/2014/05/10/WhenToMock.html
❖ http://blog.8thlight.com/uncle-bob/2014/05/14/TheLittleMocker.html
❖ http://martinfowler.com/articles/mocksArentStubs.html
❖ http://php-and-symfony.matthiasnoback.nl/2014/07/test-doubles/
❖ http://www.jmock.org/oopsla2004.pdf
MUCHAS
GRACIAS!!

Más contenido relacionado

La actualidad más candente

P2 actividades 2 y 3 infografía palabras reservadas
P2 actividades 2 y 3 infografía  palabras reservadasP2 actividades 2 y 3 infografía  palabras reservadas
P2 actividades 2 y 3 infografía palabras reservadasErick Ramirez
 
Curso TDD Ruby on Rails #01: Introducción al testing
Curso TDD Ruby on Rails #01: Introducción al testingCurso TDD Ruby on Rails #01: Introducción al testing
Curso TDD Ruby on Rails #01: Introducción al testingAlberto Perdomo
 
Curso TDD Ruby on Rails #03: Tests unitarios
Curso TDD Ruby on Rails #03: Tests unitariosCurso TDD Ruby on Rails #03: Tests unitarios
Curso TDD Ruby on Rails #03: Tests unitariosAlberto Perdomo
 
Modulo 2 interbloqueos
Modulo 2 interbloqueosModulo 2 interbloqueos
Modulo 2 interbloqueosJOSE MENDOZA
 
5. otros aspectos de la programación orientada a objetos
5. otros aspectos de la programación orientada a objetos5. otros aspectos de la programación orientada a objetos
5. otros aspectos de la programación orientada a objetosHectorMamani
 
Investigacion sobre carga de metodos
Investigacion sobre carga de metodosInvestigacion sobre carga de metodos
Investigacion sobre carga de metodosArisRojas4
 
3 java sesin 3 pps
3 java sesin 3 pps3 java sesin 3 pps
3 java sesin 3 ppsajplbe
 

La actualidad más candente (12)

PCJ Sesión 9: Threads
PCJ Sesión 9: ThreadsPCJ Sesión 9: Threads
PCJ Sesión 9: Threads
 
P2 actividades 2 y 3 infografía palabras reservadas
P2 actividades 2 y 3 infografía  palabras reservadasP2 actividades 2 y 3 infografía  palabras reservadas
P2 actividades 2 y 3 infografía palabras reservadas
 
Curso TDD Ruby on Rails #01: Introducción al testing
Curso TDD Ruby on Rails #01: Introducción al testingCurso TDD Ruby on Rails #01: Introducción al testing
Curso TDD Ruby on Rails #01: Introducción al testing
 
Best Practices
Best PracticesBest Practices
Best Practices
 
Qunit CookBook español
Qunit CookBook españolQunit CookBook español
Qunit CookBook español
 
Curso TDD Ruby on Rails #03: Tests unitarios
Curso TDD Ruby on Rails #03: Tests unitariosCurso TDD Ruby on Rails #03: Tests unitarios
Curso TDD Ruby on Rails #03: Tests unitarios
 
Modulo 2 interbloqueos
Modulo 2 interbloqueosModulo 2 interbloqueos
Modulo 2 interbloqueos
 
5. otros aspectos de la programación orientada a objetos
5. otros aspectos de la programación orientada a objetos5. otros aspectos de la programación orientada a objetos
5. otros aspectos de la programación orientada a objetos
 
Capitulo 8 9-10
Capitulo 8 9-10Capitulo 8 9-10
Capitulo 8 9-10
 
Investigacion sobre carga de metodos
Investigacion sobre carga de metodosInvestigacion sobre carga de metodos
Investigacion sobre carga de metodos
 
Actividad 3
Actividad 3Actividad 3
Actividad 3
 
3 java sesin 3 pps
3 java sesin 3 pps3 java sesin 3 pps
3 java sesin 3 pps
 

Similar a Doble o nada

DeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishDeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishJordi Llonch
 
DeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishDeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishJordi Llonch
 
DeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishDeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishAkamon Engineering
 
Probando aplicaciones AngularJS
Probando aplicaciones AngularJSProbando aplicaciones AngularJS
Probando aplicaciones AngularJSRodrigo Pimentel
 
Conceptos básicos de Unit Test
Conceptos básicos de Unit Test Conceptos básicos de Unit Test
Conceptos básicos de Unit Test Juan Vladimir
 
20180313 Keep Calm And Test Your Code RiojaDotNet
20180313 Keep Calm And Test Your Code RiojaDotNet20180313 Keep Calm And Test Your Code RiojaDotNet
20180313 Keep Calm And Test Your Code RiojaDotNetalbertortizcape
 
To mock or not to mock
To mock or not to mockTo mock or not to mock
To mock or not to mockEloi Poch
 
Como escribir buenos tests al hacer TDD
Como escribir buenos tests al hacer TDDComo escribir buenos tests al hacer TDD
Como escribir buenos tests al hacer TDDHernan Wilkinson
 
Test Automation .NET
Test Automation .NETTest Automation .NET
Test Automation .NETAngel Nuñez
 
Unit Testing with Mock Objects
Unit Testing with Mock ObjectsUnit Testing with Mock Objects
Unit Testing with Mock ObjectsAngel Nuñez
 
Clean code 9
Clean code 9Clean code 9
Clean code 9540deg
 
Pruebas-OCW.pdf
Pruebas-OCW.pdfPruebas-OCW.pdf
Pruebas-OCW.pdflgarcias
 
Pruebas unitarias 7mo -b
Pruebas unitarias   7mo -bPruebas unitarias   7mo -b
Pruebas unitarias 7mo -bJairoSimbaa
 
Cómo diagnosticar problemas de rendimiento en entornos LAMP
Cómo diagnosticar problemas de rendimiento en entornos LAMPCómo diagnosticar problemas de rendimiento en entornos LAMP
Cómo diagnosticar problemas de rendimiento en entornos LAMPJavier Carranza
 

Similar a Doble o nada (20)

DeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishDeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - Spanish
 
DeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishDeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - Spanish
 
DeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - SpanishDeSymfonyDay 2014 - To mock or not to mock - Spanish
DeSymfonyDay 2014 - To mock or not to mock - Spanish
 
Probando aplicaciones AngularJS
Probando aplicaciones AngularJSProbando aplicaciones AngularJS
Probando aplicaciones AngularJS
 
Conceptos básicos de Unit Test
Conceptos básicos de Unit Test Conceptos básicos de Unit Test
Conceptos básicos de Unit Test
 
20180313 Keep Calm And Test Your Code RiojaDotNet
20180313 Keep Calm And Test Your Code RiojaDotNet20180313 Keep Calm And Test Your Code RiojaDotNet
20180313 Keep Calm And Test Your Code RiojaDotNet
 
Practicas tecnicas
Practicas tecnicasPracticas tecnicas
Practicas tecnicas
 
Test unitarios
Test unitariosTest unitarios
Test unitarios
 
Practicas técnicas
Practicas técnicasPracticas técnicas
Practicas técnicas
 
To mock or not to mock
To mock or not to mockTo mock or not to mock
To mock or not to mock
 
Como escribir buenos tests al hacer TDD
Como escribir buenos tests al hacer TDDComo escribir buenos tests al hacer TDD
Como escribir buenos tests al hacer TDD
 
Unit testing consejos
Unit testing   consejosUnit testing   consejos
Unit testing consejos
 
Test Automation .NET
Test Automation .NETTest Automation .NET
Test Automation .NET
 
Unit Testing with Mock Objects
Unit Testing with Mock ObjectsUnit Testing with Mock Objects
Unit Testing with Mock Objects
 
Clean code 9
Clean code 9Clean code 9
Clean code 9
 
Pruebas-OCW.pdf
Pruebas-OCW.pdfPruebas-OCW.pdf
Pruebas-OCW.pdf
 
BDD & Cucumber
BDD & CucumberBDD & Cucumber
BDD & Cucumber
 
Pruebas unitarias 7mo -b
Pruebas unitarias   7mo -bPruebas unitarias   7mo -b
Pruebas unitarias 7mo -b
 
Cómo diagnosticar problemas de rendimiento en entornos LAMP
Cómo diagnosticar problemas de rendimiento en entornos LAMPCómo diagnosticar problemas de rendimiento en entornos LAMP
Cómo diagnosticar problemas de rendimiento en entornos LAMP
 
Optimizacion de software
Optimizacion de softwareOptimizacion de software
Optimizacion de software
 

Doble o nada

  • 1. Doble o Nada Jesús Miguel Benito Calzada - PHP Cáceres
  • 2. Jesús Miguel Benito Calzada Software developer @ Intexdev beni0888@hotmail.com @beni0888 https://github.com/beni0888 ¿Quién soy?
  • 3.
  • 4. ¿De qué va esto? ❖ Unit testing ❖ PHPUnit ❖ Test Doubles ❖ Mockery
  • 5. DISCLAIMER ❖ Esta es mi manera de hacer las cosas, seguro que las hay mejores. ❖ No soy un experto en testing, pero a veces hago tests ;). ❖ Esto no trata de TDD, aunque es posible que el término aparezca en más de una ocasión.
  • 6. Unit Test Herramienta que nos permite comprobar el correcto funcionamiento de una unidad de código. Esto sirve para asegurar que cada una de las partes del sistema funciona correctamente por separado. Debe ser: ❖ Atómico ❖ Independiente ❖ Inocuo ❖ Rápido
  • 7. Atómico ❖ El test prueba la mínima cantidad de funcionalidad posible . ❖ Un solo comportamiento del método o función. ❖ Distintos caminos de ejecución => un test para cada camino
  • 8. Independiente ❖ El resultado de la ejecución de un test no debe depender de otros. ❖ No debe ser parte de una secuencia de tests que deba ejecutarse en un orden determinado
  • 9. Inocuo ❖ La ejecución de un test no debe alterar el estado del sistema (ficheros, base de datos, etc) => no mancha :) ❖ El resultado debe ser el mismo ya lo ejecutemos una o mil veces => idempotente
  • 10. Rápido ❖ Normalmente lanzaremos un gran número de tests en cada ejecución ❖ Tener que esperar resulta improductivo y aburrido => no haríamos test (menos de los que ya hacemos :P) ❖ Un test unitario debería ejecutarse en una pequeña fracción de segundo
  • 11. Características ATRIP ❖ Automatic: pueden ser corridos mediante un simple comando. ❖ Thorough (minucioso): cuanto más testemos mejor. ❖ Repeatable: podemos ejecutarlo una y mil veces con el mismo resultado. ❖ Independant: el resultado de un test no debe depender de otros. ❖ Professional: debemos ser igual de exigentes al escribir los tests de lo que lo somos al escribir nuestra lógica de negocio (prohibidas ñapas en los tests)
  • 12. Un poco de terminología
  • 13. Un poco de terminología ❖ SUT: Subject/System Under Test. ❖ Colaboradores: objetos que interactúan con el SUT y que necesita para implementar su funcionalidad. ❖ Fixtures: Algo así como los elementos de contexto, datos u objetos que necesitamos para construir el escenario que requiere el test.
  • 14. Anatomía de un unit test: AAA ❖ Arrange: configuración y/o carga de fixtures, instanciar colaboradores, etc. ❖ Act: ejercitar la funcionalidad que queremos testear. ❖ Assert: afirmar que el estado es el esperado
  • 15. PHPUnit ❖ Versión para php de los frameworks xUnit ❖ Creado por Sebastian Bergman ❖ Estándar de facto en php ❖ https://phpunit.de/
  • 17. TestCase ❖ Los tests se organiza en clases que heredan de la clase PHPUnit_Framework_TestCase ❖ Cada unit test es un método de la clase ❖ Para que un método sea considerado un test, su nombre debe llevar el sufijo test o debe estar marcado con la anotación @test
  • 18. setUp() y tearDown() ❖ setUp(): método que se ejecuta antes de cada test, pude servirnos para la fase de arrangement. ❖ tearDown(): método que se ejecuta después de cada test, puede utilizarse para labores de limpieza
  • 20. Excepciones ❖ Podemos hacer que el test falle si no se lanza una excepción
  • 21. Organización de los tests ❖ Fichero de configuración: ❖ Sistema de archivos: • phpunit.xml.dist • phpunit.xml
  • 22.
  • 23. Mocks ❖ Son objetos que imitan a otros objetos de nuestro sistema y que utilizamos en su lugar a la hora de hacer tests => “Test Doubles” ❖ Suelen reemplazar a los colaboradores en los tests unitarios ❖ En ocasiones pueden incluso reemplazar al SUT!! ❖ Permiten aislar la funcionalidad del SUT de la de los colaboradores ❖ Facilitan la configuración del escenario del test ❖ Permiten que los tests unitarios sean rápidos
  • 24. ¿Todos los Test Doubles son Mocks?
  • 25. ¿Todos los Test Doubles son Mocks? ❖ No ❖ Mock es una palabra empleada en el lenguaje informal para referirse a los test doubles en general. ❖ Existen varios tipos específicos de test doubles. ❖ Los mocks son un tipo específico de test double ❖ Vamos a verlos!
  • 26. Dummies ❖ Es un test doble que sólo se usa para rellenar parámetros de un constructor o método ❖ Nunca es usado dentro del la funcionalidad del SUT que vamos a ejercitar (al menos en el camino de ejecución que vamos a testear) ❖ En teoría sus métodos deben devolver NULL (si lo generamos a mano implementando una interface) ❖ Generalmente implementarán una interface
  • 27. Dummies Algunos ejemplos robados de http://php-and-symfony.matthiasnoback.nl/2014/07/test-doubles/ ❖ Dummies con phpunit: ❖ Ejemplo con mockery:
  • 28. Stubs ❖ Son test doubles que devuelve un valor fijo predeterminado en las llamadas a sus métodos ❖ No tiene en cuenta los argumentos pasados a los métodos (no se verifican) ❖ No tiene en cuenta el número de llamadas realizadas a los métodos
  • 29. Stubs
  • 30. Fakes ❖ Son una especie de stubs con cierta lógica distinta de la verdadera lógica del colaborador al que imita y normalmente más ligera ❖ Suelen emplearse cuando el SUT depende de un colaborador que no está disponible, es lento o simplemente dificulta el test ❖ Tampoco tiene en cuenta el número de llamadas a los métodos ❖ No realiza verificación de parámetros ❖ Pueden volverse tan complicados que requieran sus propios tests unitarios
  • 31. Fakes
  • 32. Spies ❖ Es una especie de fake o stub que que recaba información en las llamadas realizadas a sus métodos y permite inspeccionarla después ❖ Puede por ejemplo registrar qué métodos han sido llamados, número de veces, parámetros recibidos, etc.
  • 33. Spies
  • 34. Mocks ❖ Son un tipo muy especial de test double ❖ Se parece a los spies en el sentido de que registran las llamadas realizadas y los parámetros pasados ❖ Es mucho más potente que un spy ❖ Permite algo que el resto no: validación del comportamiento o interacción
  • 35. Mocks ❖Permite definir un conjunto de expectativas para validar: • Llamadas a los métodos • Parámetros pasados: número, tipo y valor • Número de veces llamado: exacto, mínimo, máximo • Orden de las llamadas ❖Pueden actuar como stubs devolviendo valores ❖Existen un gran número de librerías que permiten generar mocks de objetos (o interfaces) al vuelo, en php: phpunit, mockery, prophecy, fake, etc.
  • 36. Mocks
  • 37. To mock or not to mock? ❖ Los mocks son una herramienta muy potente y su uso tiene sus pros y sus contras ❖ Su uso puede llevar a sobrespecificación y dar lugar a tests frágiles ❖ El uso o no de los mismos permite diferenciar dos tipos de validación: • Validación del estado: propiedades de objetos, valores devueltos, etc. • Validación del comportamiento o interacción: llamadas a métodos, número de veces, orden, argumentos, etc. ❖ Esto a su vez da lugar a dos estilos de TDD: • Classicist TDD (Chicago School): basado en la validación del estado • Mockist TDD (London School): basado en la validación del comportamiento
  • 38. Classic TDD vs Mockist TDD ❖ TDD con mocks permite seguir un diseño outside-in: • Se empieza creando un test para la capa más externa del sistema y se mockean lo colaboradores • Posteriormente se desarrollan tests para los colaboradores • Así sucesivamente se van descubriendo los objetos del sistema y su api ❖ TDD clásico suele seguir un diseño middle-out: • Se elige una característica del dominio por la que comenzar la implementación • Se avanza desarrollando el resto de objetos del dominio necesarios para el funcionamiento de dicha característica • Una vez desarrollada se construye la capa superior
  • 39. Classic TDD vs Mockist TDD ❖ El uso de mocks suele hace que la fase de preparación del tests sea más costosa ya que hay que definir los mocks y sus expectativas ❖ En el TDD clásico se suele reutilizar el código de configuración entre varios tests, este suele ir el método setUp ❖ Un fallo en un objeto de un sistema con mocks causará fallos sólo en los tests del objeto ❖ Un fallo en un objeto de un sistema clásico puede provocar fallos en los tests de todos los objetos que tienen al objeto como colaborador => puede ser complicado localizar el fallo ❖ Hacer tests de granularidad fina minimizará este problema ❖ Como ventaja los tests clásicos comprueban la integración real de los objetos del sistema
  • 40. Classic TDD vs Mockist TDD ❖ Los tests con mocks están muy acoplados a la implementación del SUT => cambios en la implementación provocarán ruptura de los tests ❖ Los tests clásicos sólo se preocupan del estado, da igual cual sea la implementación si el resultado es el esperado ❖ La refactorización del código testado con mocks conllevará un trabajo importante de refactorización de los tests
  • 41.
  • 42. Mockery ❖ Librería php que nos permite crear mocks al vuelo ❖ https://github.com/padraic/mockery ❖ http://docs.mockery.io/en/latest/ ❖ Instalación: $ composer require mockery/mockery
  • 43. Integración con phpunit public function tearDown() { Mockery::close(); } <listeners> <listener class="MockeryAdapterPhpunitTestListener"></listener> </listeners> ❖ En el fichero de configuración: ❖ En cada test case: ❖ Si no hacemos esto no se validarán las expectativas
  • 44. Mockeando $mock = Mockery::mock(‘foo');
 // Mock llamado “foo” $mock = Mockery::mock(array('foo'=>1,'bar'=>2));
 // Mock sin nombre con array de espectativas $mock = Mockery::mock('foo', array('foo'=>1,'bar'=>2));
 // Mock “foo” con espectativas $mock = Mockery::mock('stdClass');
 // Mockeando clases $mock = Mockery::mock(‘PSRLogLoggerInterface’);
 // Mockeando interfaces $mock = Mockery::mock('stdClass, MyInterface1, MyInterface2');
 // Mockeando una clase e implementando interfaces
  • 45. Definiendo espectativas • shouldReceive(method_name)
 // El método debe ser llamado • shouldReceive(method1, method2, ...)
 //Varios métodos • shouldReceive(array('method1'=>1, 'method2'=>2, …))
 // Los métodos deben ser llamados y devolverán estos valores • with(arg1, arg2, ...) / withArgs(array(arg1, arg2, ...))
 // validando los parámetros recibidos • withAnyArgs(), withNoArgs() //… • andReturn(value) / andReturn(value1, value2, ...)
 // estableciendo valores devueltos en la llamada / llamadas • andReturnNull() / andReturn([NULL]) / andReturnValues(array) / andReturnUsing(closure, ...)
  • 46. Definiendo espectativas • andThrow(Exception) / andThrow(exception_name, message)
 // lanzando excepciones en la llamada • andSet(name, value1) / set(name, value1)
 // establecer valor de atributos en la llamada • passthru() // llama a la verdadera implementación del método • zeroOrMoreTimes() / once() / twice() / times(n) / never() / atLeast() / atMost() / between(min, max)
 // estableciendo el número de llamadas: exacto, mínimo, máximo, entre • ordered() / ordered($group)
 // define el orden de llamadas o el orden dentro de un grupo • globally() // el orden se entre todos los mocks, no sólo el actual • byDefault() // define expectativas por defecto, que puden ser sobreescritas en los tests concretos
  • 47. Validando argumentos • with(‘some-value’) // con un valor concreto • with(Mockery::any()) OR with(anything()) // cualquier valor • with(Mockery::type('resource')) OR with(resourceValue()) OR with(typeOf(‘resource')) // con un tipo concreto • with(Mockery::on(closure)) // validando mediante un closure • with('/^foo/') OR with(matchesPattern(‘/^foo/')) // regexp • with(Mockery::ducktype('foo', ‘bar')) //cualquier objeto que contenga los métodos indicados • with(Mockery::mustBe(2)) OR with(identicalTo(2)) // === • with(Mockery::not(2)) OR with(not(2)) // negando • with(Mockery::anyOf(1, 2)) OR with(anyOf(1,2)) // varias opciones
  • 48. Validando argumentos • with(Mockery::notAnyOf(1, 2)); // que no esté entre las opciones • with(Mockery::subset(array(0 => ‘foo’))); 
 // un array que contenga este subconjunto • with(Mockery::contains(value1, value2)); 
 // un array que contenga estos valores • with(Mockery::hasKey(key)); 
 // un array con una clave • with(Mockery::hasValue(value));
 // con un valor
  • 49. Partial Mocks • $mock = Mockery::mock(‘MyClass[foo, bar]’); Método tradicional, mockea los métodos foo y bar de la clase,
 hay que definir su comportamiento mediante expectativas. • $mock = Mockery::mock(‘MyClass')->makePartial(); Método pasivo, las llamadas son enviadas a los métodos originales,
 excepto si se han definido expectativas para el método. • $mock = Mockery::mock(new MyClass); Mock parcial con proxy, crea un proxy que intercepta las llamadas en 
 lugar de heredar del objeto a mockear, esto permite mockear clases 
 declaradas “final” o con métodos “final”. El inconveniente es que no validará los type hints ya que no extiende la clase a mockear. ❖ Permiten mockear sólo algunos métodos de los objetos
  • 50. Y más en la documentación… ❖ Mockear atributos públicos ❖ Métodos estáticos ❖ Conservar el paso de parámetros por refencia ❖ Mockear cadenas de Demeter ❖ Grabación de interacciones con objetos reales ❖ …
  • 52. Referencias ❖ http://blog.8thlight.com/uncle-bob/2014/05/10/WhenToMock.html ❖ http://blog.8thlight.com/uncle-bob/2014/05/14/TheLittleMocker.html ❖ http://martinfowler.com/articles/mocksArentStubs.html ❖ http://php-and-symfony.matthiasnoback.nl/2014/07/test-doubles/ ❖ http://www.jmock.org/oopsla2004.pdf
  • 53.