Unité testant un servlet Java

Je voudrais savoir quelle serait la meilleure façon de faire des tests unitaires d’une servlet.

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

Est-il possible de le faire simplement en utilisant des outils classiques tels que JUnit, ou de préférence TestNG? Dois-je intégrer un serveur Tomcat ou quelque chose du genre?

Essayez HttpUnit , même si vous finirez probablement 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).

La plupart du temps, je teste les Servlets et JSP via des «tests d’intégration» plutôt que des tests unitaires purs. Il existe un grand nombre de modules complémentaires disponibles pour JUnit / TestNG, notamment:

  • 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 mieux pour de nombreux projets)
  • JWebUnit (se trouve au dessus des autres outils de test et essaie de les simplifier – celui que je préfère)
  • WatiJ et Selenium (utilisez votre navigateur pour faire le test, qui est plus lourd mais réaliste)

Ceci est un test JWebUnit pour un simple Servlet de traitement des commandes qui traite les entrées du formulaire ‘orderEntry.html’. Il attend un identifiant client, un nom de client et un ou plusieurs articles de commande:

public class OrdersPageTest { private static final Ssortingng 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 regardé les réponses postées et j’ai pensé que je publierais une solution plus complète qui montrerait comment faire les tests en utilisant GlassFish intégré et son plugin Apache Maven.

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

Je regardais un autre post sur un servlet d’image pour les balises JSP / JSF juste avant que je voie cette question. J’ai donc combiné la solution que j’ai utilisée depuis l’autre poste avec une version complète testée par l’unité pour cet article.

Comment tester

Apache Maven a un cycle de vie bien défini qui inclut le test . Je vais utiliser ceci avec un autre cycle de vie appelé integration-test d’ integration-test pour implémenter ma solution.

  1. Désactiver les tests d’unité de cycle de vie standard dans le plugin surefire.
  2. Ajouter integration-test dans le cadre des exécutions du plugin surefire
  3. Ajoutez le plug-in GlassFish Maven au POM.
  4. Configurez GlassFish pour qu’il s’exécute pendant le cycle de vie du integration-test .
  5. Exécuter des tests unitaires (tests d’intégration).

Plugin GlassFish

Ajoutez ce plugin dans le cadre de .

   org.glassfish maven-embedded-glassfish-plugin 3.1.1   target/${project.build.finalName} 8080  test  true    start  pre-integration-test  start deploy    stop  post-integration-test  undeploy stop     

Plugin Surefire

Ajoutez / modifiez le plugin dans le cadre de .

   org.apache.maven.plugins maven-surefire-plugin 2.12.4   true    integration-test   test    false     

HTMLUnit

Ajoutez des tests d’intégration comme 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 avec GlassFish 3.1.1 Embedded avec JUnit 4.x et HtmlUnit 2.x et j’ai placé le projet complet à télécharger sur Bitbucket ici: image-servlet

Si vous avez des questions, veuillez laisser un commentaire. Je pense que ceci est un exemple complet à utiliser comme base de tout test que vous prévoyez pour les servlets.

Appelez-vous les méthodes doPost et doGet manuellement dans les tests unitaires? Si c’est le cas, vous pouvez remplacer les méthodes HttpServletRequest pour fournir des objects simulés.

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

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

 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 superclasse de servlet de base et de faire en sorte que tous les tests unitaires de servlets l’étendent.

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

Cette implémentation d’un test JUnit pour la méthode de servlet doPost () repose uniquement sur la bibliothèque Mockito pour la simulation des instances de HttpRequest , HttpResponse , HttpSession , ServletResponse et ServletResponse . 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:

  org.mockito mockito-all 1.9.5  

JUnit test:

 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 Ssortingng SYMBOL_PARAMETER_KEY = "symbol"; private static final Ssortingng STARTRANGE_PARAMETER_KEY = "startRange"; private static final Ssortingng ENDRANGE_PARAMETER_KEY = "endRange"; private static final Ssortingng INTERVAL_PARAMETER_KEY = "interval"; private static final Ssortingng 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"); Ssortingng symbol = request.getParameter(SYMBOL_PARAMETER_KEY); Ssortingng startRange = request.getParameter(STARTRANGE_PARAMETER_KEY); Ssortingng endRange = request.getParameter(ENDRANGE_PARAMETER_KEY); Ssortingng interval = request.getParameter(INTERVAL_PARAMETER_KEY); Ssortingng 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.setAtsortingbute("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); } } 

Mis à jour en février 2018: OpenBrace Limited a fermé ses portes et son produit ObMimic n’est plus pris en charge.

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

Vous pouvez en effet l’utiliser pour appeler directement les méthodes doGet / doPost à partir des tests JUnit ou TestNG, et pour tester toutes les méthodes internes même si elles font référence aux parameters ServletContext ou utilisent des parameters de session (ou toute autre fonctionnalité de l’API Servlet).

Cela ne nécessite pas de conteneur externe ou incorporé, cela ne vous limite pas à des tests “d’intégration” basés sur HTTP, et contrairement aux simulations à usage général, le comportement complet de l’API Servlet est “intégré”, donc vos tests peuvent être ” l’état “plutôt que l’interaction” (par exemple, vos tests ne doivent pas dépendre de la séquence précise des appels d’API Servlet effectués par votre code, ni de vos attentes quant à la réponse de l’API Servlet à 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 Web ObMimic .