unitarios unitarias unit test studio pruebas ejemplos ejemplo curso unit-testing servlets testing junit mockito

unit testing - unitarias - Cómo probar mi servlet usando JUnit



test unitarios (10)

Creé un sistema web usando Java Servlets y ahora quiero hacer pruebas JUnit. Mi dataManager es solo una pieza básica de código que lo envía a la base de datos. ¿Cómo probarías un Servlet con JUnit?

Mi ejemplo de código que permite a un usuario registrarse / registrarse, que se envía desde mi página principal a través de AJAX:

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ // Get parameters String userName = request.getParameter("username"); String password = request.getParameter("password"); String name = request.getParameter("name"); try { // Load the database driver Class.forName("com.mysql.jdbc.Driver"); //pass reg details to datamanager dataManager = new DataManager(); //store result as string String result = dataManager.register(userName, password, name); //set response to html + no cache response.setContentType("text/html"); response.setHeader("Cache-Control", "no-cache"); //send response with register result response.getWriter().write(result); } catch(Exception e){ System.out.println("Exception is :" + e); } }


Actualizado en febrero de 2018: OpenBrace Limited se ha cerrado y su producto ObMimic ya no es compatible.

Aquí hay otra alternativa, usando la biblioteca ObMimic de OpenBrace de prueba-dobles API de Servlet (revelación: soy su desarrollador).

package com.openbrace.experiments.examplecode.5434419; import static org.junit.Assert.*; import com.openbrace.experiments.examplecode.5434419.YourServlet; import com.openbrace.obmimic.mimic.servlet.ServletConfigMimic; import com.openbrace.obmimic.mimic.servlet.http.HttpServletRequestMimic; import com.openbrace.obmimic.mimic.servlet.http.HttpServletResponseMimic; import com.openbrace.obmimic.substate.servlet.RequestParameters; import org.junit.Before; import org.junit.Test; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Example tests for {@link YourServlet#doPost(HttpServletRequest, * HttpServletResponse)}. * * @author Mike Kaufman, OpenBrace Limited */ public class YourServletTest { /** The servlet to be tested by this instance''s test. */ private YourServlet servlet; /** The "mimic" request to be used in this instance''s test. */ private HttpServletRequestMimic request; /** The "mimic" response to be used in this instance''s test. */ private HttpServletResponseMimic response; /** * Create an initialized servlet and a request and response for this * instance''s test. * * @throws ServletException if the servlet''s init method throws such an * exception. */ @Before public void setUp() throws ServletException { /* * Note that for the simple servlet and tests involved: * - We don''t need anything particular in the servlet''s ServletConfig. * - The ServletContext isn''t relevant, so ObMimic can be left to use * its default ServletContext for everything. */ servlet = new YourServlet(); servlet.init(new ServletConfigMimic()); request = new HttpServletRequestMimic(); response = new HttpServletResponseMimic(); } /** * Test the doPost method with example argument values. * * @throws ServletException if the servlet throws such an exception. * @throws IOException if the servlet throws such an exception. */ @Test public void testYourServletDoPostWithExampleArguments() throws ServletException, IOException { // Configure the request. In this case, all we need are the three // request parameters. RequestParameters parameters = request.getMimicState().getRequestParameters(); parameters.set("username", "mike"); parameters.set("password", "xyz#zyx"); parameters.set("name", "Mike"); // Run the "doPost". servlet.doPost(request, response); // Check the response''s Content-Type, Cache-Control header and // body content. assertEquals("text/html; charset=ISO-8859-1", response.getMimicState().getContentType()); assertArrayEquals(new String[] { "no-cache" }, response.getMimicState().getHeaders().getValues("Cache-Control")); assertEquals("...expected result from dataManager.register...", response.getMimicState().getBodyContentAsString()); } }

Notas:

  • Cada "imitador" tiene un objeto "mimicState" por su estado lógico. Esto proporciona una distinción clara entre los métodos API de Servlet y la configuración e inspección del estado interno de la mímica.

  • Es posible que se sorprenda de que la verificación de Content-Type incluya "charset = ISO-8859-1". Sin embargo, para el código "doPost" dado, esto es según el Javadoc API Servlet, y el propio método getContentType de HttpServletResponse, y el encabezado real Content-Type producido en, por ejemplo, Glassfish 3. Puede que no te des cuenta si usas objetos simulados normales y tu propias expectativas del comportamiento de la API. En este caso, probablemente no importe, pero en casos más complejos, este es el tipo de comportamiento API no anticipado que puede convertirse en una burla de burlas.

  • He usado response.getMimicState().getContentType() como la forma más sencilla de verificar el tipo de contenido e ilustrar el punto anterior, pero de hecho podrías verificar "text / html" si lo deseas (usando response.getMimicState().getContentTypeMimeType() ). También funciona el control del encabezado Content-Type de la misma manera que para el encabezado Cache-Control.

  • Para este ejemplo, el contenido de respuesta se verifica como datos de caracteres (con esto usando la codificación del escritor). También podríamos verificar que se usó el Writer de la respuesta en lugar de su OutputStream (usando response.getMimicState().isWritingCharacterContent() ), pero response.getMimicState().isWritingCharacterContent() que solo nos preocupa el resultado resultante, y no nos importa qué Las llamadas a API lo produjeron (aunque eso también podría verificarse ...). También es posible recuperar el contenido del cuerpo de la respuesta en bytes, examinar el estado detallado del Writer / OutputStream, etc.

Hay detalles completos de ObMimic y una descarga gratuita en el sitio web de OpenBrace . O puede contactarme si tiene alguna pregunta (los detalles de contacto están en el sitio web).


En primer lugar, en una aplicación real, nunca obtendría información de conexión a la base de datos en un servlet; lo configuraría en su servidor de aplicaciones.

Sin embargo, hay formas de probar Servlets sin tener un contenedor ejecutándose. Una es usar objetos simulados. Spring proporciona un conjunto de simulacros muy útiles para cosas como HttpServletRequest, HttpServletResponse, HttpServletSession, etc.

http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/mock/web/package-summary.html

Usando estos simulacros, podrías probar cosas como

¿Qué sucede si el nombre de usuario no está en la solicitud?

¿Qué sucede si el nombre de usuario está en la solicitud?

etc

Podrías hacer cosas como estas:

import static org.junit.Assert.assertEquals; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; public class MyServletTest { private MyServlet servlet; private MockHttpServletRequest request; private MockHttpServletResponse response; @Before public void setUp() { servlet = new MyServlet(); request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); } @Test public void correctUsernameInRequest() throws ServletException, IOException { request.addParameter("username", "scott"); request.addParameter("password", "tiger"); servlet.doPost(request, response); assertEquals("text/html", response.getContentType()); // ... etc } }


En primer lugar, probablemente debería refactorizarlo un poco para que DataManager no se cree en el código doPost. Debería probar Dependency Injection para obtener una instancia. (Ver el video de Guice para una buena introducción a DI.). Si le dicen que comience a probar todo, DI es imprescindible.

Una vez que se hayan inyectado sus dependencias, puede probar su clase de forma aislada.

Para probar realmente el servlet, hay otros hilos más antiguos que han discutido esto ... intente here y here .


Encuentro que las pruebas de Selenium son más útiles con pruebas de integración o funcionales (de extremo a extremo). Estoy trabajando con el intento de usar org.springframework.mock.web , pero no estoy muy lejos. Adjunto un controlador de muestra con un paquete de prueba jMock .

Primero, el controlador:

package com.company.admin.web; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.support.SessionStatus; import com.company.admin.domain.PaymentDetail; import com.company.admin.service.PaymentSearchService; import com.company.admin.service.UserRequestAuditTrail; import com.company.admin.web.form.SearchCriteria; /** * Controls the interactions regarding to the refunds. * * @author slgelma * */ @Controller @SessionAttributes({"user", "authorization"}) public class SearchTransactionController { public static final String SEARCH_TRANSACTION_PAGE = "searchtransaction"; private PaymentSearchService searchService; //private Validator searchCriteriaValidator; private UserRequestAuditTrail notifications; @Autowired public void setSearchService(PaymentSearchService searchService) { this.searchService = searchService; } @Autowired public void setNotifications(UserRequestAuditTrail notifications) { this.notifications = notifications; } @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE) public String setUpTransactionSearch(Model model) { SearchCriteria searchCriteria = new SearchCriteria(); model.addAttribute("searchCriteria", searchCriteria); notifications.transferTo(SEARCH_TRANSACTION_PAGE); return SEARCH_TRANSACTION_PAGE; } @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="cancel") public String cancelSearch() { notifications.redirectTo(HomeController.HOME_PAGE); return "redirect:/" + HomeController.HOME_PAGE; } @RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="execute") public String executeSearch( @ModelAttribute("searchCriteria") @Valid SearchCriteria searchCriteria, BindingResult result, Model model, SessionStatus status) { //searchCriteriaValidator.validate(criteria, result); if (result.hasErrors()) { notifications.transferTo(SEARCH_TRANSACTION_PAGE); return SEARCH_TRANSACTION_PAGE; } else { PaymentDetail payment = searchService.getAuthorizationFor(searchCriteria.geteWiseTransactionId()); if (payment == null) { ObjectError error = new ObjectError( "eWiseTransactionId", "Transaction not found"); result.addError(error); model.addAttribute("searchCriteria", searchCriteria); notifications.transferTo(SEARCH_TRANSACTION_PAGE); return SEARCH_TRANSACTION_PAGE; } else { model.addAttribute("authorization", payment); notifications.redirectTo(PaymentDetailController.PAYMENT_DETAIL_PAGE); return "redirect:/" + PaymentDetailController.PAYMENT_DETAIL_PAGE; } } } }

Luego, la prueba:

package test.unit.com.company.admin.web; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.integration.junit4.JMock; import org.jmock.integration.junit4.JUnit4Mockery; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.support.SessionStatus; import com.company.admin.domain.PaymentDetail; import com.company.admin.service.PaymentSearchService; import com.company.admin.service.UserRequestAuditTrail; import com.company.admin.web.HomeController; import com.company.admin.web.PaymentDetailController; import com.company.admin.web.SearchTransactionController; import com.company.admin.web.form.SearchCriteria; /** * Tests the behavior of the SearchTransactionController. * @author slgelma * */ @RunWith(JMock.class) public class SearchTransactionControllerTest { private final Mockery context = new JUnit4Mockery(); private final SearchTransactionController controller = new SearchTransactionController(); private final PaymentSearchService searchService = context.mock(PaymentSearchService.class); private final UserRequestAuditTrail notifications = context.mock(UserRequestAuditTrail.class); private final Model model = context.mock(Model.class); /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { controller.setSearchService(searchService); controller.setNotifications(notifications); } @Test public void setUpTheSearchForm() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; context.checking(new Expectations() {{ oneOf(model).addAttribute( with(any(String.class)), with(any(Object.class))); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.setUpTransactionSearch(model); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void cancelSearchTest() { final String target = HomeController.HOME_PAGE; context.checking(new Expectations(){{ never(model).addAttribute(with(any(String.class)), with(any(Object.class))); oneOf(notifications).redirectTo(with(any(String.class))); }}); String nextPage = controller.cancelSearch(); assertThat("Controller is not requesting the correct form", nextPage, containsString(target)); } @Test public void executeSearchWithNullTransaction() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(null); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(true)); never(model).addAttribute(with(any(String.class)), with(any(Object.class))); never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId()); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void executeSearchWithEmptyTransaction() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(""); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(true)); never(model).addAttribute(with(any(String.class)), with(any(Object.class))); never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId()); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void executeSearchWithTransactionNotFound() { final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE; final String badTransactionId = "badboy"; final PaymentDetail transactionNotFound = null; final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(badTransactionId); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(false)); atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class))); oneOf(searchService).getAuthorizationFor(with(any(String.class))); will(returnValue(transactionNotFound)); oneOf(result).addError(with(any(ObjectError.class))); oneOf(notifications).transferTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", target, equalTo(nextPage)); } @Test public void executeSearchWithTransactionFound() { final String target = PaymentDetailController.PAYMENT_DETAIL_PAGE; final String goodTransactionId = "100000010"; final PaymentDetail transactionFound = context.mock(PaymentDetail.class); final SearchCriteria searchCriteria = new SearchCriteria(); searchCriteria.seteWiseTransactionId(goodTransactionId); final BindingResult result = context.mock(BindingResult.class); final SessionStatus status = context.mock(SessionStatus.class); context.checking(new Expectations() {{ allowing(result).hasErrors(); will(returnValue(false)); atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class))); oneOf(searchService).getAuthorizationFor(with(any(String.class))); will(returnValue(transactionFound)); oneOf(notifications).redirectTo(with(any(String.class))); }}); String nextPage = controller.executeSearch(searchCriteria, result, model, status); assertThat("Controller is not requesting the correct form", nextPage, containsString(target)); } }

Espero que esto ayude.


Otro enfoque sería crear un servidor incrustado para "alojar" su servlet, lo que le permite escribir llamadas en su contra con bibliotecas destinadas a hacer llamadas a servidores reales (la utilidad de este enfoque depende en cierta medida de la facilidad con la que puede realizar programática "legítima"). llamadas al servidor: estaba probando un punto de acceso JMS (Java Messaging Service), del que abundan los clientes).

Hay un par de rutas diferentes que puede tomar, las dos habituales son Tomcat y embarcadero.

Advertencia: algo a tener en cuenta al elegir el servidor para insertar es la versión de servlet-api que está utilizando (la biblioteca que proporciona clases como HttpServletRequest). Si usa 2.5, encontré que Jetty 6.x funciona bien (que es el ejemplo que daré a continuación). Si está utilizando servlet-api 3.0, el material incluido tomcat-7 parece ser una buena opción, sin embargo tuve que abandonar mi intento de usarlo, ya que la aplicación que estaba probando usa servlet-api 2.5. Intentar mezclar los dos dará como resultado NoSuchMethod y otras excepciones similares al intentar configurar o iniciar el servidor.

Puede configurar un servidor como este (Jetty 6.1.26, servlet-api 2.5):

public void startServer(int port, Servlet yourServletInstance){ Server server = new Server(port); Context root = new Context(server, "/", Context.SESSIONS); root.addServlet(new ServletHolder(yourServletInstance), "/servlet/context/path"); //If you need the servlet context for anything, such as spring wiring, you coudl get it like this //ServletContext servletContext = root.getServletContext(); server.start(); }


Puede hacer esto usando Mockito para que el simulacro devuelva los parámetros correctos, verifique que efectivamente fueron llamados (opcionalmente, especifique el número de veces), escriba el ''resultado'' y verifique que sea correcto.

import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.io.*; import javax.servlet.http.*; import org.apache.commons.io.FileUtils; import org.junit.Test; public class TestMyServlet extends Mockito{ @Test public void testServlet() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); when(request.getParameter("username")).thenReturn("me"); when(request.getParameter("password")).thenReturn("secret"); StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); when(response.getWriter()).thenReturn(writer); new MyServlet().doPost(request, response); verify(request, atLeast(1)).getParameter("username"); // only if you want to verify username was called... writer.flush(); // it may not have been flushed yet... assertTrue(stringWriter.toString().contains("My expected string")); }


Solo en caso de que las respuestas anteriores ya no funcionen en las versiones más nuevas de Mockito en lugar de usar mock() y when() Mockito.mock() y Mockito.when() deberían usarse


Use Selenium para pruebas de unidades basadas en web. Hay un complemento de Firefox llamado Selenium IDE que puede registrar acciones en la página web y exportar a pruebas JUnit que utiliza Selenium RC para ejecutar el servidor de prueba.


EDITAR : Cactus ahora es un proyecto muerto: http://attic.apache.org/projects/jakarta-cactus.html

Es posible que desee mirar cactus.

http://jakarta.apache.org/cactus/

Descripción del Proyecto

Cactus es un marco de prueba simple para probar en unidades el código java del lado del servidor (Servlets, EJBs, Tag Libs, Filters, ...).

La intención de Cactus es reducir el costo de escribir pruebas para el código del lado del servidor. Utiliza JUnit y lo extiende.

Cactus implementa una estrategia en el contenedor, lo que significa que las pruebas se ejecutan dentro del contenedor.


public class WishServletTest { WishServlet wishServlet; HttpServletRequest mockhttpServletRequest; HttpServletResponse mockhttpServletResponse; @Before public void setUp(){ wishServlet=new WishServlet(); mockhttpServletRequest=createNiceMock(HttpServletRequest.class); mockhttpServletResponse=createNiceMock(HttpServletResponse.class); } @Test public void testService()throws Exception{ File file= new File("Sample.txt"); File.createTempFile("ashok","txt"); expect(mockhttpServletRequest.getParameter("username")).andReturn("ashok"); expect(mockhttpServletResponse.getWriter()).andReturn(new PrintWriter(file)); replay(mockhttpServletRequest); replay(mockhttpServletResponse); wishServlet.doGet(mockhttpServletRequest, mockhttpServletResponse); FileReader fileReader=new FileReader(file); int count = 0; String str = ""; while ( (count=fileReader.read())!=-1){ str=str+(char)count; } Assert.assertTrue(str.trim().equals("Helloashok")); verify(mockhttpServletRequest); verify(mockhttpServletResponse); } }