Muchas veces nos hemos puesto a probar nuestro código mediante tests unitarios y nos hemos encontrado que la clase tiene una o varias dependencias, pero como el propio nombre lo dice, los tests unitarios son unitarios y solo tenemos que probar las clase, no sus dependencias. Por tanto la forma de probar nuestra clase independientemente de sus dependencias, es hacer “dobles” de estas dependencias que hagan o bien nada o bien tengan un comportamiento que nosotros deseamos que tenga para determinadas circunstancias, por ejemplo, probamos una clase que interactúa con la capa de datos, podemos tener varios casos:
- Nos devuelve datos correctamente
- No nos devuelve datos
- Algún tipo de error
- Otros
Para simular este comportamiento, lo que hacemos son los tests dobles, tenemos 4 tipos según nuestras necesidades:
- Dummy Object
- Fake Object
- Stubs
- Mocks
Dummy Object
En el caso del dummy object, lo que se hace es pasar como argumento pero nunca se usa realmente. Normalmente, los objetos dummy se usan sólo para satisfacer una dependencia, el objeto no haría ningún tipo de lógica.
//Implementacion del Dummy class DummyCar implements Car { public function run() { } } //Clase que queremos testear, dependiente de Car class Race { public function addCar(Car $car) { //No hacemos comprobaciones ni operaciones, //solo ponemos a true la propiedad run para poder //continuar el flujo de la ejecución $this->running = true; } } //Test... $race = new Race(); $race->addCar(new DummyCar());
Fake
El objeto fake tiene una implementación concreta pero, por lo general, no es el funcionamiento real, simplemente da el resultado esperado para el test pero sin realizar la lógica real.
class FakeCar implements Car { public function run() { } }
Stub
El objeto stub proporciona respuestas predefinidas (según nos convenga en el test) a llamadas hechas en nuestros tests unitarios. Los stubs pueden también grabar información sobre las llamadas; tal como una pasarela de email que recuerda cuántos mensajes envió.
class FakeCar implements Car { public function isRunning() { return true; //O false, dependiendo de lo que necesitemos en el test } }
Mock
Estos son objetos más complejos, son objetos preprogramados con expectativas que conforman la especificación de cómo se espera que se reciban las llamadas. Estos objetos suelen ser implementados con librerias como PHPUnit o PHPSpec. En este caso veremos un ejemplo de PHPUnit:
//En una clase de PHPUnit... $mockObject = $this->getMock('Car'); $mockObject->expects($this->once()) ->method('setColor') ->with('#CC00AA') ->will($this->returnValue(true));
En este caso estamos diciendo que el objeto ha de recibir una llamada (una sola vez) a su método setColor con el parámetro #CC00AA y bajo estas circunstancias el resultado de la ejecución devolverá ‘true’.