Question

J'aimerais savoir quel serait le meilleur moyen de tester un servlet par unité.

Tester les méthodes internes n’est pas un problème tant qu’elles ne font pas référence au contexte du servlet, mais qu’en est-il du test des méthodes doGet / doPost et de la méthode interne qui fait référence au contexte ou utilise des paramètres de session?

Existe-t-il un moyen de le faire simplement en utilisant des outils classiques tels que JUnit ou de préférence TestNG? Ai-je besoin d'intégrer un serveur Tomcat ou quelque chose du genre?

Était-ce utile?

La solution

Essayez HttpUnit , mais vous finirez probablement par écrire des tests automatisés qui sont davantage des "tests d'intégration" ( d'un module) à des "tests unitaires" (d'une seule classe).

Autres conseils

La plupart du temps, je teste les servlets et les JSP via des "tests d'intégration" plutôt que des tests unitaires purs. Un grand nombre d’add-ons pour JUnit / TestNG sont disponibles, notamment:

  • HttpUnit (le niveau le plus ancien et le plus connu, très bas, qui peut être bon ou mauvais en fonction de vos besoins )
  • HtmlUnit (niveau supérieur à HttpUnit, ce qui est préférable pour de nombreux projets)
  • JWebUnit (figure parmi d'autres outils de test et tente de les simplifier - celui que je préfère)
  • WatiJ et Selenium (utilisez votre navigateur pour effectuer les tests, qui sont plus lourds mais plus réalistes)

Il s'agit d'un test JWebUnit pour un simple servlet de traitement de commandes, qui traite les entrées à partir du formulaire 'orderEntry.html'. Il attend un identifiant client, un nom de client et un ou plusieurs postes de commande:

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

J'ai consulté les réponses publiées et j'ai pensé publier une solution plus complète qui montre comment effectuer les tests à l'aide de GlassFish intégré et de son plug-in Apache Maven.

J'ai écrit le processus complet sur mon blog Utilisation de GlassFish 3.1.1 Intégré à JUnit 4.x et HtmlUnit 2.x et placé le projet complet au téléchargement sur Bitbucket ici: image-servlet

Je regardais un autre article sur une servlet d’image pour balises JSP / JSF juste avant de voir cette question. J'ai donc combiné la solution utilisée dans l'autre poste avec une version complète testée pour ce poste.

Comment tester

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 implémenter ma solution.

  1. Désactivez les tests d'unité de cycle de vie standard dans le plugin Surefire.
  2. Ajouter <build> dans le cadre de l'exécution du plugin surefire
  3. Ajoutez le plug-in GlassFish Maven au POM.
  4. Configurez GlassFish pour s’exécuter au cours du <=> cycle de vie.
  5. Exécuter des tests unitaires (tests d'intégration).

plug-in GlassFish

Ajouter ce plugin dans le cadre du <=>.

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

plug-in Surefire

Ajouter / modifier le plug-in dans le cadre du <=>.

        <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

Ajoutez 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 Utilisation de GlassFish 3.1.1 Intégré à JUnit 4.x et HtmlUnit 2.x et placé le projet complet au téléchargement sur Bitbucket ici: image-servlet

Si vous avez des questions, laissez un commentaire. Je pense que c’est un exemple complet à utiliser comme base de tout test que vous planifiez pour les servlets.

Appelez-vous les méthodes doPost et doGet manuellement dans les tests unitaires? Si tel est le cas, vous pouvez remplacer les méthodes HttpServletRequest pour fournir des objets fictifs.

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

     ...
}

Le HttpServletRequestWrapper . est une classe Java de commodité. 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 même préférable de placer les méthodes de création fictives dans une super-classe de servlets de base et de faire en sorte que tous les tests unitaires des servlets les étendent.

Mockrunner ( http://mockrunner.sourceforge.net/index.html ) peut le faire. ce. Il fournit un conteneur fictif J2EE pouvant être utilisé pour tester les servlets. Il peut également être utilisé pour tester à l'unité d'autres codes côté serveur tels que les EJB, JDBC, JMS, Struts. J'ai uniquement utilisé les fonctionnalités JDBC et EJB moi-même.

Cette implémentation d'un test JUnit pour la méthode de servlet doPost () repose uniquement sur la bibliothèque Mockito pour simuler des 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 Mockito Maven:

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

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

Mise à jour en février 2018: OpenBrace Limited a été fermée et son produit ObMimic n'est plus pris en charge. / p>

Une autre solution consiste à utiliser ma bibliothèque ObMimic , spécialement conçue pour le test unitaire des servlets. Il fournit des implémentations Java complètes de toutes les classes de l'API Servlet, que vous pouvez configurer et inspecter si nécessaire pour vos tests.

Vous pouvez en effet l'utiliser pour appeler directement des méthodes doGet / doPost à partir de tests JUnit ou TestNG, et pour tester toute méthode interne, même si elles font référence à ServletContext ou utilisent des paramètres de session (ou toute autre fonctionnalité de l'API Servlet).

Cela n’a pas besoin d’un conteneur externe ou intégré, ne vous limite pas à une intégration " basée sur HTTP plus large " tests, et contrairement aux simulacres d'usage général, il a le comportement complet de l'API Servlet & "cuit au four dans &"; vos tests peuvent donc être & "à l'état &"; plutôt que "interaction &"; (par exemple, vos tests ne doivent pas dépendre de la séquence précise d'appels d'API Servlet effectués par votre code, ni de vos propres attentes quant à la réponse de l'API Servlet à chaque appel) .

Voici un exemple simple dans ma réponse à Comment tester mon servlet à l'aide de JUnit . Pour plus de détails et un téléchargement gratuit, consultez le site Web ObMimic .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top