Test Unitario De Un DAO Que Utiliza Axis2
Contenido
Objetivo
Probar en forma unitaria un DAO que realiza una invocación a un Web Service a través de un cliente generado con Axis2.
Se chequea comprobando que los parametros con los que se invoca sean los esperados y se verifica el comportamiento del DAO frente a excepciones del stub de Axis2.
Libs necesarios
- Los especificados en JARs necesarios para un cliente con Axis2
Framework utilizado
Escenario
Se expone un supuesto servicio de tickets a través de un Web Service con un método agregar(Ticket) que al ser invocado devuelve un acuse de recibo. La invocación al Web Service se encapsula en un DAO que utiliza el código Java generado con Axis2.
- TicketServiceStub: Clase que genera Axis2 desde donde se invoca el Web Service y que contiene en forma de clases internas el resto de las clases necesarias para construir la invocación.
- TicketDto: Es una clase interna de TicketServiceStub que agrupa los valores con que se invocará el Web Service.
- TicketDao: Invoca al WebService a través del stub TicketServiceStub de Axis2 y transforma la respuesta a un AcuseRecibo.
- Ticket: Es la entidad que va a recibir el DAO para invocar el Web Service.
El DAO
La consideración a tener en cuenta es aislar la creación del Stub en un método para poder sobreescribirlo durante los tests para inyectarle un Mock Object en su lugar.
public class WsDao {
public boolean agregar(Ticket ticket) {
//Crea el dto que contiene los parametros para invocar el servicio. TicketDao ticketDto = new TicketDto(); ticketDto.setMotivo(ticket.getMotivo);
//Obtiene la instancia del stub, en el caso del test, obtendrá un Mock. TicketServiceStub ticketServiceStub = crearStub();
//Invoca el servicio llamando al stub. boolean respuesta = ticketServiceStub.agregar(ticketDto);
//Devuelve la respuesta del servicio return respuesta;
}
//Este metodo crea el stub, deberá ser sobreescrito por el test unitario para devolver //un Mock en su Lugar. protected TicketServiceStub crearStub() { return new TicketServiceStub(); }
}
El Test Unitario
public class WsDaoTest {
@Mock private TicketServiceStub stub;
private WsDao instance;
//El atributo que se utilizará para capturar el parámetro que recibe el stub al momento de //invocar el servicio. private TicketDto ticketDto;
//Mock para dar diferentes comportamientos a la respuesta del stub; @Mock private AgregarResponse agregarResponse;
@Before public void setup() { ... //init mocks Mockito.initMocks.....
//Al Mock Object del stub se le dá un determinado comportamiento para poder capturar //el parámetro con que se invoca el método "agregar(TicketDto)" ya que esto sucede //internamente en el dao y TicketDto contiene los atributos que se quieren verificar. //stub doAnswer(new Answer<AgregarResponse>() { public AgregarResponse agregar(InvocationOnMock invocation) { ticketDtoCapturado = (TicketDto) invocation.getArguments()[0]; return agregarResponse; } }) .when(ticketServiceStub) .agregar(isA(TicketDto.class));
//Aca es donde se sobreescribe el método que crea el stub en el DAO para //que en su lugar devuelva el Mock "stub" que crea y tiene como atributo //esta clase. //Dentro de la nueva implementación del método también se encuentra el código //necesario para comprobar que la implementación original funciona correctamente. //De lo contrario quedará "a la sombra" de la nueva implementación. //instance instance = new WsDao() { protected TicketServiceStub crearStub() { assertNotNull(super.crearStub()); return stub; } } }
@Test public void agregar_conParametrosEsperados_retornaTrue() {
//La obtención de un ticket desde un ObjectMother Ticket ticket = TicketObjectMother.crearTicketCompleto();
//Se configura una respuesta true a la invocación del servicio doReturn(true) .when(agregarResponse) .get_return();
//La llamada al método en cuestión boolean respuesta = instance.agregar(ticket);
//Durante la llamada "agregar(ticket)" el Mock de ticketServiceStub captura el parámetro con que //fue invocado y lo pasa a ticketDtoCapturado. //Los assert verifican que dicho dto contenga los parámetros esperados assertNotNull(ticketDtoCapturado); assertEquals(ticket.getMotivo(), ticketDtoCapturado.getMotivo());
//Verifica que la respuesta del servicio sea true assertTrue(respuesta);
}
@Test public void agregar_conParametrosErroneos_retornaFalse() {
//La obtención de un ticket desde un ObjectMother Ticket ticket = TicketObjectMother.crearTicketCompleto();
//Se configura una respuesta false a la invocación del servicio //Esto en realidad no es necesario porque es el comportamiento //por default de los Mock Objects creados con Mockito //doReturn(false) //.when(agregarResponse) //.get_return();
//La llamada al método en cuestión boolean respuesta = instance.agregar(ticket);
assertFalse(respuesta);
}
//Cuando falla la instanciacion del stub @Test (expected = AxisFault.class) public void agregar_conAxisFault_tiraAxisFault() { //Es necesario redefinir el método crearStub del dao para que tire una excepción al intentar //crear el stub como sucedería en un caso real. instance = new WsDao() { protected TicketServiceStub crearStub() { throw new AxisFault("blah"); } };
Ticket ticket = TicketObjectMother.crearTicketCompleto(); instance.agregar(ticket);
}
//Cuando falla la invocacion al servicio @Test (expected = RemoteException.class) public void agregar_conRemoteException_tiraRemoteException() {
//Es necesario cambiar el comportameinto del método agregar del stub para que tire //una excepción al ser invocado. doThrow(new RemoteException("blah")) .when(ticketServiceStub) .agregar(isA(TicketDto.class));
Ticket ticket = TicketObjectMother.crearTicketCompleto(); instance.agregar(ticket);
}
static class WsDaoModificado extends WsDao {
protected TicketServiceStub crearStub() { TicketServiceStub stub = super.createStub(); asertNotNull(stub); return ticketServiceStub; }
}
}