L'unité teste un Servlet Java

j'aimerais savoir quelle serait la meilleure façon de tester un servlet à l'unité.

tester les méthodes internes n'est pas un problème tant qu'elles ne se réfèrent pas au contexte servlet, mais qu'en est-il de tester les méthodes doGet/doPost ainsi que la méthode interne qui se réfèrent au contexte ou qui utilisent des paramètres de session?

y a-t-il un moyen de le faire simplement en utilisant des outils classiques tels que JUnit, ou préférablement TestNG? Ai-je besoin d'incorporer un serveur tomcat ou quelque chose comme ça?

52
demandé sur John Yeary 2008-09-18 12:21:21

7 réponses

Try HttpUnit , bien qu'il soit probable que vous finissiez par écrire des tests automatisés qui sont plus des "tests d'intégration" (d'un module) que des "tests unitaires" (d'une seule classe).

12
répondu Peter Hilton 2008-09-18 08:22:57

la plupart du temps, je teste des Servlets et des JSP via des "tests D'intégration" plutôt que des tests unitaires purs. Il ya un grand nombre d'add-ons pour JUnit / TestNG disponibles, y compris:

  • HttpUnit (le plus ancien et le plus connu, niveau très bas qui peut être bon ou mauvais selon vos besoins)
  • HtmlUnit (niveau supérieur à HttpUnit, ce qui est meilleur pour de nombreux projets)
  • JWebUnit (Assis sur d'autres outils de test et tente de les simplifier - celui que je préfère)
  • WatiJ et le Sélénium (utilisez votre navigateur pour effectuer les tests, ce qui est plus lourd mais réaliste)

il s'agit d'un test JWebUnit pour un simple Servlet de traitement des commandes qui traite les entrées de la commande 'orderEntry.html". Il s'attend à un numéro de client, nom du client et un ou plusieurs articles de l'ordre du jour:

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;
}
44
répondu Garth Gilmour 2011-07-28 19:45:32

j'ai regardé les réponses postées et j'ai pensé que je posterais une solution plus complète qui montre réellement comment faire les tests en utilisant GlassFish intégré et son plugin Apache Maven.

j'ai écrit le processus complet sur mon blog en utilisant GlassFish 3.1.1 Embedded with JUnit 4.x et HtmlUnit 2.x et placé le projet complet à télécharger sur Bitbucket ici: image-servlet

je regardais un autre post sur un servlet d'image pour les tags JSP/JSF juste avant de voir cette question. Donc j'ai combiné la solution que j'ai utilisée de l'autre poste avec une version complète de l'unité testé pour ce poste.

Comment faire l'essai

Apache Maven a un cycle de vie bien défini qui inclut test . Je vais l'utiliser avec un autre cycle de vie appelé integration-test pour mettre en œuvre ma solution.

  1. Désactiver le test de l'Unité de cycle de vie standard dans le plugin surefire.
  2. ajouter integration-test dans le cadre des exécutions du surefire-plugin
  3. ajouter le plugin GlassFish Maven au POM.
  4. Configure GlassFish à exécuter pendant le cycle de vie integration-test .
  5. exécuter des essais unitaires (essais d'intégration).

Glassfish Plugin

ajouter ce plugin sous partie du <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>

Surefire Plugin

Ajouter/modifier le plugin dans le cadre du <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

ajouter des tests d'intégration comme dans l'exemple ci-dessous.

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

j'ai écrit le processus complet sur mon blog en utilisant GlassFish 3.1.1 Embedded with JUnit 4.x et HtmlUnit 2.x et placé le complet projet à télécharger sur Bitbucket ici: image-servlet

Si vous avez des questions, veuillez laisser un commentaire. Je pense que c'est un exemple complet pour vous d'utiliser comme base de toute analyse, vous êtes planification pour les servlets.

10
répondu John Yeary 2017-02-22 14:31:35

appelez-vous les méthodes doPost et doGet manuellement dans les tests unitaires? Si c'est le cas, vous pouvez outrepasser les méthodes HttpServletRequest pour fournir des objets mock.

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

     ...
}

le HttpServletRequestWrapper est une classe de Java. Je vous suggère de créer une méthode utilitaire dans vos tests unitaires pour créer les requêtes HTTP simulées:

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

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

il est encore mieux de mettre les méthodes de création simulées dans un servlet de base superclass et faire tous les tests de l'Unité de servlets pour l'étendre.

6
répondu Marcio Aguiar 2008-09-18 08:43:28

Mockrunner ( http://mockrunner.sourceforge.net/index.html ) peut le faire. Il fournit un conteneur J2EE simulé qui peut être utilisé pour tester les Servlets. Il peut également être utilisé pour tester d'autres codes côté serveur comme EJBs, JDBC, JMS, Struts. Je n'ai utilisé que les capacités JDBC et EJB.

6
répondu Kris Pruden 2008-09-18 16:06:13

cette implémentation D'un test de JUnit pour la méthode servlet doPost() s'appuie uniquement sur la bibliothèque Mockito pour les instances de HttpRequest , HttpResponse , HttpSession , ServletResponse et RequestDispatcher . Remplacez les clés de paramètre et l'instance JavaBean par celles qui correspondent aux valeurs référencées dans le fichier JSP associé à partir duquel doPost() est appelé.

dépendance de Mockito Maven:

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

test de 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);
    }
}
3
répondu Bob Basmaji 2016-07-26 12:15:58

Updated Feb 2018: OpenBrace Limited a fermé , et son produit ObMimic n'est plus supporté.

une autre solution consiste à utiliser Ma bibliothèque ObMimic , qui est spécifiquement conçue pour les essais unitaires de servlets. Il fournit des implémentations Java simples et complètes de toutes les classes D'API Servlet, et vous pouvez configurer et inspecter ces implémentations si nécessaire pour vos tests.

vous pouvez en effet utiliser il appelle directement les méthodes doGet/doPost des tests JUnit ou TestNG, et teste toutes les méthodes internes même si elles se réfèrent au ServletContext ou utilisent les paramètres de session (ou toute autre fonctionnalité de L'API de Servlet).

cela n'a pas besoin d'un conteneur externe ou intégré, ne vous limite pas à des tests D'intégration plus larges basés sur HTTP, et contrairement aux moqueries Générales, il a le comportement complet de L'API de Servlet "cuit", de sorte que vos tests peuvent être basés sur "l'état" plutôt que sur"l'interaction". (par exemple, vos tests n'ont pas à se fier à la séquence précise des appels à L'API de Servlet faite par votre code, ni à vos propres attentes quant à la façon dont l'API de Servlet répondra à chaque appel).

il y a un exemple simple dans ma réponse à Comment tester mon servlet en utilisant JUnit . Pour plus de détails et un téléchargement gratuit, consultez le site ObMimic .

0
répondu Mike Kaufman 2018-02-27 14:20:05