Supongamos que nos encontramos en la construcción mediante TDD de la aplicación especificada en el ejercicio Cotxox.
La clase Carrera
depende de la lógica que por SRP corresponde a las clases que implementan las interfaces Conductora
, PoolConductora
y Tarifa
.
Si las historias de usuario más valiosas suponen la implementación de la clase Carrera
llegaremos el momento en el que necesitaremos la información proporcionada por los componentes de los que depende. Si la lógica o algoritmia y el diseño de estas clases supone cierta dificultad que no puede ser resuelta de manera ágil, podemos aplicar los principios SOLID para mantener las interfaces públicas de dichos componentes adheridas a dichos principios y "mockear" o crear métodos stub que sustituyan dichas funcionalidades.
Utilizamos mockito como framework en Junit para construir el comportamiento descrito.
Mockito, además, nos ofrece la posibilidad de realizar cierta reflexión sobre el comportamiento de las clases que "mockea", "espiar" en las tripas de los objetos mockeados, para comprobar que el comportamiento es como suponemos, al igual que hacía Slimer en cazafantasmas.
@Test
public void asignarConductor(){
// Utilizamos las interfaces para crear los mocks
// de Conductora y PoolConductoras
// pues en ellas disponemos de los
// métodos abstractos sin implementación.
Conductora mockConductor = mock(Conductora.class);
when(mockConductor.getNombre()).thenReturn("Samantha");
carrera.setConductor(null);
assertNull(carrera.getConductor());
PoolConductoras mockPool = mock(PoolConductoras.class);
when(mockPool.asignarConductor()).thenReturn(mockConductor);
carrera.asignarConductor(mockPool);
// verificamos que asignarConductor() de PoolConductora
// ha sido invocado a través de carrera.asignarConductor()
verify(mockPool).asignarConductor();
assert(carrera.getConductor()!=null);
assertEquals("Samantha", carrera.getConductor().getNombre());
}
Aquí un tutorial con ejemplos básicos de uso. Conviene que lo leas para usar los mocks de manera eficiente:
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
No está de más leerse esta RefCard:
https://dzone.com/refcardz/mockito
Testear aplicaciones en el Mundo Real TM es un poco más complicado y, si a la dificultad añadida por la arquitectura cliente-servidor, le sumamos la de una arquitectura basada en microservicios, lejos de las placenteras aplicaciones monolíticas, entonces es necesario disponer de un conocimiento más profundo de la API del framework de testig y mock.
En mi proyeto grpc-tourism-receptive encontrarás a mockito y Slimer en acción en una aplicación basada en el framework gRPC de Google para la construcción de aplicaciones basadas en microservicios, implementando el patrón GoF observer para implementar la lógica de un receptivo turístico al estilo mallorquín ;)
https://github.com/dfleta/grpc-tourism-receptive
Aquí los casos test: test grpc
En los test de los componentes "clientes" de los servidores se concentra el uso de mockito, para simular que disponemos de servidores de pago, crisal y UFOs que proveen de los objetos que necesitamos para completar el desarrollo de los componentes cliente indendientemente de su estado de desarrollo y funcionamiento:
@Test
public void UfoOf_test() {
// Mensaje CreditCard al servicio
CreditCard card = CreditCard.newBuilder()
.setOwner("Rick")
.setNumber("111111111111")
.build();
AtomicReference<CreditCard> cardDelivered = new AtomicReference<CreditCard>();
// Mock de la respuesta /mensaje Processed del servicio
Ufo responseUfo = Ufo.newBuilder()
.setId("unox")
.setCardNumbe("111111111111")
.setFee(500)
.build();
// Fake service
UfosParkImplBase assignImpl = new UfosParkImplBase() {
@Override
public void ufoOf(CreditCard request, StreamObserver<org.elsmancs.grpc.Ufo> response) {
// para chequear que la construccion del Ufo en el client se realiza OK
cardDelivered.set(request);
// return the Ufo
response.onNext(responseUfo);
// Specify that we’ve finished dealing with the RPC.
response.onCompleted();
}
};
serviceRegistry.addService(assignImpl);
String ufoID = client.UfoOf("Rick", "111111111111");
assertEquals(card, cardDelivered.get());
assertEquals(responseUfo.getId(), ufoID);
}
En mi proyecto ejemplo sobre el patrón proxy (+ singleton), patrón flyweight, herencia por prototipos en JS, prototipos delegados, extensión dinámica de objetos, constructor functions proxy pattern mr meeseeks encontrarás ejemplos de uso de mocking en front, utilizando la librería Jest para JavaScript /EmacScript.
Es conveniente leer el manual de la librería sobre funciones mock
En estos casos test se hace uso de las funcionalidades de Jest para mockear componentes JavaScript:
test('fullfillRequest ejecuta this.accion()', () => {
// MOCK FUNCTIONS con IMPLEMENTATIONS
/**
* La funcion accion que necesita fulfillRequest
* ha de ser creada previamente por makeRequest()
* e inyectada en el objeto meeseeks.
* No podemos depender de la implementacion de makeRequest()
* para pasar este test => mockear la funcion accion() que
* inyecta makeRequest() e inyectarla a mano en el objeto meeseeks
*/
const accionMock = jest
.fn()
.mockImplementation( () => "open" + " " + "Jerry's head")
.mockName('accionMock') // mensajes especificos en test errors outputs
// inyecto en el objeto la funcion mock
meeseeks.accion = accionMock;
expect(meeseeks).toHaveProperty('accion'); // primera invocacion de la funcion accionMock
// el objeto meeseeks invoca a la funcion mock == segunda invocacion de la función mock
expect(meeseeks.fulfillRequest()).toEqual(expect.stringMatching("open" + " " + "Jerry's head" + " All done!!"))
// accionMock ha debido ser llamada desde fulfillRequest()
expect(accionMock).toHaveBeenCalled();
// La funcion ha sido llamada exactamente dos veces: toHaveProperty + fullfillRequest
expect(accionMock.mock.calls.length).toBe(2);
// El valor devuelto en la segunda llamada a la funcion ha sido "open Jerry's head"
expect(accionMock.mock.results[1].value).toBe("open Jerry's head");
// La funcion ha sido llamada con un cierto contexto `this`: el objeto `meeseeks`
expect(accionMock.mock.contexts[0]).toBe(meeseeks);
// Ek primer argumento de la ultima llamada a la funcion ha sido 'undefined', accion()
expect(accionMock.mock.lastCall[0]).toBe(undefined);
});