2008-09-18 14 views
51

Me gustaría saber cuál sería la mejor manera de hacer pruebas unitarias de un servlet.Unidad de prueba de Java Servlet

Probar los métodos internos no es un problema siempre que no se refieran al contexto de servlet, pero ¿qué hay de probar los métodos doGet/doPost así como el método interno que hace referencia al contexto o hacer uso de parámetros de sesión ?

¿Hay alguna manera de hacer esto simplemente usando herramientas clásicas como JUnit, o preferiblemente TestNG? ¿Debo integrar un servidor Tomcat o algo así?

+0

posible duplicado de [servlets Unidad para pruebas] (http://stackoverflow.com/questions/53532/unit-testing- servlets) – Raedwald

Respuesta

12

Pruebe HttpUnit, aunque es probable que termine escribiendo pruebas automatizadas que son más 'pruebas de integración' (de un módulo) que 'pruebas unitarias' (de una sola clase).

+0

Bueno, realmente se trata más de Pruebas unitarias ya que, de ser posible, reemplazaría toda la interacción entre la clase servlet por otras clases mediante la interacción con los simuladores. – gizmo

+2

HttpUnit parece que no ha tenido cambios desde 2008, lo que sugiere que es un proyecto muerto. – Raedwald

+3

¿Hay un reemplazo más reciente para HttpUnit? – oconnor0

6

¿Está llamando manualmente a los métodos doPost y doGet en las pruebas unitarias? De ser así, puede anular los métodos HttpServletRequest para proporcionar objetos simulados.

myServlet.doGet(new HttpServletRequestWrapper() { 
    public HttpSession getSession() { 
     return mockSession; 
    } 

    ... 
} 

El HttpServletRequestWrapper es una clase Java conveniencia. Sugiero crear un método de utilidad en las pruebas unitarias para crear las peticiones http simulacros:

public void testSomething() { 
    myServlet.doGet(createMockRequest(), createMockResponse()); 
} 

protected HttpServletRequest createMockRequest() { 
    HttpServletRequest request = new HttpServletRequestWrapper() { 
     //overrided methods 
    } 
} 

Es incluso mejor que poner los métodos de creación de simulacros en una superclase del servlet base y realizar todas las pruebas unitarias servlets para extenderla.

+3

HttpServletRequestWrapper no tiene un constructor predeterminado, solo uno con un parámetro HttpServletRequest. –

43

La mayoría de las veces pruebo Servlets y JSP a través de 'Pruebas de Integración' en lugar de Pruebas unitarias puras. Hay un gran número de complementos para JUnit/TestNG disponibles, incluyendo:

  • HttpUnit (el muy bajo nivel antigua y mejor conocida que puede ser bueno o malo dependiendo de sus necesidades)
  • HtmlUnit (mayor nivel de HttpUnit, que es mejor para muchos proyectos)
  • JWebUnit (se sienta encima de otras herramientas de prueba y trata de simplificarlas - la que yo prefiero)
  • WatiJ y selenio (use su navegador para hacer la prueba, la cual es más pesado pero realista)

Esta es una prueba JWebUnit para un Servlet de procesamiento de pedidos simple que procesa la entrada desde el formulario 'orderEntry.html'. Se espera un identificador de cliente, el nombre del cliente y uno o más artículos de la orden:

public class OrdersPageTest { 
    private static final String WEBSITE_URL = "http://localhost:8080/demo1"; 

    @Before 
    public void start() { 
     webTester = new WebTester(); 
     webTester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT); 
     webTester.getTestContext().setBaseUrl(WEBSITE_URL); 
    } 
    @Test 
    public void sanity() throws Exception { 
     webTester.beginAt("/orderEntry.html"); 
     webTester.assertTitleEquals("Order Entry Form"); 
    } 
    @Test 
    public void idIsRequired() throws Exception { 
     webTester.beginAt("/orderEntry.html"); 
     webTester.submit(); 
     webTester.assertTextPresent("ID Missing!"); 
    } 
    @Test 
    public void nameIsRequired() throws Exception { 
     webTester.beginAt("/orderEntry.html"); 
     webTester.setTextField("id","AB12"); 
     webTester.submit(); 
     webTester.assertTextPresent("Name Missing!"); 
    } 
    @Test 
    public void validOrderSucceeds() throws Exception { 
     webTester.beginAt("/orderEntry.html"); 
     webTester.setTextField("id","AB12"); 
     webTester.setTextField("name","Joe Bloggs"); 

     //fill in order line one 
     webTester.setTextField("lineOneItemNumber", "AA"); 
     webTester.setTextField("lineOneQuantity", "12"); 
     webTester.setTextField("lineOneUnitPrice", "3.4"); 

     //fill in order line two 
     webTester.setTextField("lineTwoItemNumber", "BB"); 
     webTester.setTextField("lineTwoQuantity", "14"); 
     webTester.setTextField("lineTwoUnitPrice", "5.6"); 

     webTester.submit(); 
     webTester.assertTextPresent("Total: 119.20"); 
    } 
    private WebTester webTester; 
} 
6

Mockrunner (http://mockrunner.sourceforge.net/index.html) pueden hacer esto. Proporciona un contenedor simulado J2EE que se puede usar para probar Servlets. También se puede usar para probar unidades de otro código del lado del servidor como EJB, JDBC, JMS, Struts. Solo he usado las capacidades JDBC y EJB yo mismo.

+2

mockrunner no se ha actualizado desde 2009. ¿Hay alguna alternativa que se mantenga? – datguy

10

Miré las respuestas publicadas y pensé que podría publicar una solución más completa que realmente demuestre cómo hacer las pruebas utilizando GlassFish incrustado y su plugin Apache Maven.

escribí el proceso completo para arriba en mi blog Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x y se coloca el proyecto completo para su descarga en Bitbucket aquí: image-servlet

que estaba buscando en otro post en un servlet de imágenes para JSP etiquetas/JSF justo antes de que vi este pregunta.Así que combiné la solución que utilicé en la otra publicación con una versión de prueba completa para esta publicación.

Cómo probar

Apache Maven tiene un ciclo de vida bien definido que incluye test. Usaré esto junto con otro ciclo de vida llamado integration-test para implementar mi solución.

  1. Deshabilitar la prueba de ciclo de vida estándar en el plugin surefire.
  2. Agregue integration-test como parte de las ejecuciones del complemento seguro-
  3. Agregue el plugin GlassFish Maven al POM.
  4. Configure GlassFish para ejecutar durante el ciclo de vida integration-test.
  5. Pruebas de unidad de ejecución (pruebas de integración).

GlassFish Plugin

Añadir este plugin como parte de la <build>.

 <plugin> 
      <groupId>org.glassfish</groupId> 
      <artifactId>maven-embedded-glassfish-plugin</artifactId> 
      <version>3.1.1</version> 
      <configuration> 
       <!-- This sets the path to use the war file we have built in the target directory --> 
       <app>target/${project.build.finalName}</app> 
       <port>8080</port> 
       <!-- This sets the context root, e.g. http://localhost:8080/test/ --> 
       <contextRoot>test</contextRoot> 
       <!-- This deletes the temporary files during GlassFish shutdown. --> 
       <autoDelete>true</autoDelete> 
      </configuration> 
      <executions> 
       <execution> 
        <id>start</id> 
        <!-- We implement the integration testing by setting up our GlassFish instance to start and deploy our application. --> 
        <phase>pre-integration-test</phase> 
        <goals> 
         <goal>start</goal> 
         <goal>deploy</goal> 
        </goals> 
       </execution> 
       <execution> 
        <id>stop</id> 
        <!-- After integration testing we undeploy the application and shutdown GlassFish gracefully. --> 
        <phase>post-integration-test</phase> 
        <goals> 
         <goal>undeploy</goal> 
         <goal>stop</goal> 
        </goals> 
       </execution> 
      </executions> 
     </plugin> 

éxito seguro Plugin

Añadir/Modificar el plugin como parte de la <build>.

 <plugin> 
      <groupId>org.apache.maven.plugins</groupId> 
      <artifactId>maven-surefire-plugin</artifactId> 
      <version>2.12.4</version> 
      <!-- We are skipping the default test lifecycle and will test later during integration-test --> 
      <configuration> 
       <skip>true</skip> 
      </configuration> 
      <executions> 
       <execution> 
        <phase>integration-test</phase> 
        <goals> 
         <!-- During the integration test we will execute surefire:test --> 
         <goal>test</goal> 
        </goals> 
        <configuration> 
         <!-- This enables the tests which were disabled previously. --> 
         <skip>false</skip> 
        </configuration> 
       </execution> 
      </executions> 
     </plugin> 

HtmlUnit

añadir pruebas de integración como el ejemplo a continuación.

@Test 
public void badRequest() throws IOException { 
    webClient.getOptions().setThrowExceptionOnFailingStatusCode(false); 
    webClient.getOptions().setPrintContentOnFailingStatusCode(false); 
    final HtmlPage page = webClient.getPage("http://localhost:8080/test/images/"); 
    final WebResponse response = page.getWebResponse(); 
    assertEquals(400, response.getStatusCode()); 
    assertEquals("An image name is required.", response.getStatusMessage()); 
    webClient.getOptions().setThrowExceptionOnFailingStatusCode(true); 
    webClient.getOptions().setPrintContentOnFailingStatusCode(true); 
    webClient.closeAllWindows(); 
} 

escribí el proceso completo para arriba en mi blog Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x y se coloca el proyecto completo para su descarga en Bitbucket aquí: image-servlet

Si usted tiene alguna pregunta, por favor dejar un comentario. Creo que este es un ejemplo completo para usar como base de cualquier prueba que esté planificando para servlets.

0

Actualizado en febrero de 2018: OpenBrace Limited has closed down, y su producto ObMimic ya no es compatible.

Otra solución es usar mi biblioteca ObMimic, que está específicamente diseñada para la prueba unitaria de servlets. Proporciona implementaciones completas de Java simple de todas las clases de API de Servlet, y puede configurarlas e inspeccionarlas según sea necesario para sus pruebas.

Puede utilizarlo para llamar directamente a los métodos doGet/doPost de las pruebas JUnit o TestNG, y para probar cualquier método interno incluso si se refieren a ServletContext o usan parámetros de sesión (o cualquier otra característica de Servlet API).

Esto no necesita un contenedor externo o incrustado, no lo limita a pruebas de "integración" basadas en HTTP más amplias y, a diferencia de los simulacros de uso general, tiene el comportamiento API Servlet completo "integrado", por lo que las pruebas pueden basarse en "estado" en lugar de "interacción" (p. ej., sus pruebas no tienen que depender de la secuencia precisa de llamadas a API de Servlet realizadas por su código, ni de sus propias expectativas sobre cómo responderá la API de Servlet a cada llamada).

Hay un ejemplo simple en mi respuesta al How to test my servlet using JUnit.Para obtener todos los detalles y una descarga gratuita, consulte el sitio web ObMimic.

+0

"Para instalar ObMimic: descomprime el archivo ObMimic-1.1.000.zip en la ubicación donde ..." Espera, ¿qué? – Innokenty

3

Esta implementación de una prueba unitaria para doPost servlet() método se basa sólo en la biblioteca Mockito por burlarse de seguridad de instancias de HttpRequest, HttpResponse, HttpSession, ServletResponse y RequestDispatcher. Reemplace las claves de parámetros y la instancia de JavaBean por las que corresponden a los valores a los que se hace referencia en el archivo JSP asociado desde el que se llama a doPost().

Mockito Maven dependencia:

<dependency> 
     <groupId>org.mockito</groupId> 
     <artifactId>mockito-all</artifactId> 
     <version>1.9.5</version> 
</dependency> 

prueba JUnit:

import javax.servlet.RequestDispatcher; 
import javax.servlet.ServletContext; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpSession; 

import java.io.IOException; 

import static org.junit.Assert.assertFalse; 
import static org.junit.Assert.assertTrue; 
import static org.mockito.Mockito.*; 

/** 
* Unit tests for the {@code StockSearchServlet} class. 
* @author Bob Basmaji 
*/ 
public class StockSearchServletTest extends HttpServlet { 
    // private fields of this class 
    private static HttpServletRequest request; 
    private static HttpServletResponse response; 
    private static StockSearchServlet servlet; 
    private static final String SYMBOL_PARAMETER_KEY = "symbol"; 
    private static final String STARTRANGE_PARAMETER_KEY = "startRange"; 
    private static final String ENDRANGE_PARAMETER_KEY = "endRange"; 
    private static final String INTERVAL_PARAMETER_KEY = "interval"; 
    private static final String SERVICETYPE_PARAMETER_KEY = "serviceType"; 

    /** 
    * Sets up the logic common to each test in this class 
    */ 
    @Before 
    public final void setUp() { 
     request = mock(HttpServletRequest.class); 
     response = mock(HttpServletResponse.class); 

     when(request.getParameter("symbol")) 
       .thenReturn("AAPL"); 

     when(request.getParameter("startRange")) 
       .thenReturn("2016-04-23 00:00:00"); 

     when(request.getParameter("endRange")) 
       .thenReturn("2016-07-23 00:00:00"); 

     when(request.getParameter("interval")) 
       .thenReturn("DAY"); 

     when(request.getParameter("serviceType")) 
       .thenReturn("WEB"); 

     String symbol = request.getParameter(SYMBOL_PARAMETER_KEY); 
     String startRange = request.getParameter(STARTRANGE_PARAMETER_KEY); 
     String endRange = request.getParameter(ENDRANGE_PARAMETER_KEY); 
     String interval = request.getParameter(INTERVAL_PARAMETER_KEY); 
     String serviceType = request.getParameter(SERVICETYPE_PARAMETER_KEY); 

     HttpSession session = mock(HttpSession.class); 
     when(request.getSession()).thenReturn(session); 
     final ServletContext servletContext = mock(ServletContext.class); 
     RequestDispatcher dispatcher = mock(RequestDispatcher.class); 
     when(servletContext.getRequestDispatcher("/stocksearchResults.jsp")).thenReturn(dispatcher); 
     servlet = new StockSearchServlet() { 
      public ServletContext getServletContext() { 
       return servletContext; // return the mock 
      } 
     }; 

     StockSearchBean search = new StockSearchBean(symbol, startRange, endRange, interval); 
     try { 
      switch (serviceType) { 
       case ("BASIC"): 
        search.processData(ServiceType.BASIC); 
        break; 
       case ("DATABASE"): 
        search.processData(ServiceType.DATABASE); 
        break; 
       case ("WEB"): 
        search.processData(ServiceType.WEB); 
        break; 
       default: 
        search.processData(ServiceType.WEB); 
      } 
     } catch (StockServiceException e) { 
      throw new RuntimeException(e.getMessage()); 
     } 
     session.setAttribute("search", search); 
    } 

    /** 
    * Verifies that the doPost method throws an exception when passed null arguments 
    * @throws ServletException 
    * @throws IOException 
    */ 
    @Test(expected = NullPointerException.class) 
    public final void testDoPostPositive() throws ServletException, IOException { 
     servlet.doPost(null, null); 
    } 

    /** 
    * Verifies that the doPost method runs without exception 
    * @throws ServletException 
    * @throws IOException 
    */ 
    @Test 
    public final void testDoPostNegative() throws ServletException, IOException { 
     boolean throwsException = false; 
     try { 
      servlet.doPost(request, response); 
     } catch (Exception e) { 
      throwsException = true; 
     } 
     assertFalse("doPost throws an exception", throwsException); 
    } 
} 
+0

'verify (session) .setAttribute (" field "," value ")' también es posiblemente una buena afirmación. – Mahdi