Pregunta

Me gustaría saber cuál sería la mejor manera de realizar pruebas unitarias de un servlet.

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

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

¿Fue útil?

Solución

Intentar Unidad HTTP, aunque es probable que termine escribiendo pruebas automatizadas que sean más "pruebas de integración" (de un módulo) que "pruebas unitarias" (de una sola clase).

Otros consejos

La mayor parte del tiempo pruebo Servlets y JSP mediante 'Pruebas de integración' en lugar de pruebas unitarias puras.Hay una gran cantidad de complementos para JUnit/TestNG disponibles, que incluyen:

  • Unidad HTTP (el más antiguo y conocido, nivel muy bajo que puede ser bueno o malo dependiendo de tus necesidades)
  • Unidad HTML (nivel superior que HttpUnit, que es mejor para muchos proyectos)
  • Unidad JWeb (se ubica encima de otras herramientas de prueba e intenta simplificarlas, la que prefiero)
  • watij y Selenium (use su navegador para realizar las pruebas, que es más pesado pero realista)

Esta es una prueba de JWebUnit para un servlet de procesamiento de pedidos simple que procesa la entrada del formulario 'orderEntry.html'.Espera una identificación de cliente, un nombre de cliente y uno o más artículos del pedido:

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;
}

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

Escribí el proceso completo en mi blog. Usando GlassFish 3.1.1 integrado con JUnit 4.x y HtmlUnit 2.x y puse el proyecto completo para descargar en Bitbucket aquí: servlet de imagen

Estaba mirando otra publicación sobre un servlet de imágenes para etiquetas JSP/JSF justo antes de ver esta pregunta.Así que combiné la solución que usé en la otra publicación con una versión completa probada por unidad 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. Deshabilite las pruebas unitarias de ciclo de vida estándar en el complemento seguro.
  2. Agregar integration-test como parte de las ejecuciones del complemento surefire
  3. Agregue el complemento GlassFish Maven al POM.
  4. Configure GlassFish para que se ejecute durante el integration-test ciclo vital.
  5. Ejecutar pruebas unitarias (pruebas de integración).

Complemento GlassFish

Agregue este complemento como parte del <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>

Complemento seguro

Agregar/modificar el complemento como parte del <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>

Unidad HTML

Agregue pruebas de integración como en el ejemplo siguiente.

@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 en mi blog. Usando GlassFish 3.1.1 integrado con JUnit 4.x y HtmlUnit 2.x y puse el proyecto completo para descargar en Bitbucket aquí: servlet de imagen

Si tienes alguna pregunta, por favor deja un comentario.Creo que este es un ejemplo completo que puede utilizar como base para cualquier prueba que esté planificando para servlets.

¿Está llamando manualmente a los métodos doPost y doGet en las pruebas unitarias?Si es 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 de conveniencia.Le sugiero que cree un método de utilidad en sus pruebas unitarias para crear solicitudes http simuladas:

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

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

Es incluso mejor colocar los métodos de creación simulados en una superclase de servlet base y realizar pruebas unitarias de todos los servlets para extenderla.

corredor simulado (http://mockrunner.sourceforge.net/index.html) puede hacer esto.Proporciona un contenedor J2EE simulado que se puede utilizar para probar servlets.También se puede utilizar para realizar pruebas unitarias de otro código del lado del servidor como EJB, JDBC, JMS, Struts.Yo solo he usado las capacidades JDBC y EJB.

Esta implementación de una prueba JUnit para el método servlet doPost() se basa únicamente en la biblioteca Mockito para simular instancias de HttpRequest, HttpResponse, HttpSession, ServletResponse y RequestDispatcher.Reemplace las claves de parámetros y la instancia de JavaBean con aquellas que correspondan a los valores a los que se hace referencia en el archivo JSP asociado desde el que se llama a doPost().

Dependencia de Mockito Maven:

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

Prueba JUnitaria:

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);
    }
}

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

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

De hecho, puede usarlo para llamar directamente a los métodos doGet/doPost desde las pruebas JUnit o TestNG, y para probar cualquier método interno incluso si hacen referencia a ServletContext o usan parámetros de sesión (o cualquier otra característica de la API de Servlet).

Esto no necesita un contenedor externo o integrado, no lo limita a pruebas más amplias de "integración" basadas en HTTP y, a diferencia de los simulacros de propósito general, tiene el comportamiento completo de la API de Servlet "integrado", por lo que sus pruebas pueden ser " basado en el estado" en lugar de basado en la "interacción" (p. ej.sus pruebas no tienen que depender de la secuencia precisa de llamadas a la 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 a Cómo probar mi servlet usando JUnit.Para obtener todos los detalles y una descarga gratuita, consulte el Obmímica sitio web.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top