2008-09-18 12 views
51

Chciałbym wiedzieć, jaki byłby najlepszy sposób na testowanie jednostkowe serwletu.Unit testowanie serwletu Java

Testowanie metod wewnętrznych nie stanowi problemu, o ile nie dotyczą one kontekstu serwletu, ale co z testowaniem metod doGet/doPost, a także metody wewnętrznej odwołującej się do kontekstu lub korzystania z parametrów sesji ?

Czy istnieje sposób, aby to zrobić za pomocą klasycznych narzędzi, takich jak JUnit, lub preferwally TestNG? Czy muszę osadzić serwer tomcat lub coś w tym stylu?

+0

możliwe duplikat [Urządzenie badające serwletów] (http://stackoverflow.com/questions/53532/unit-testing- aplety) – Raedwald

Odpowiedz

12

Wypróbuj HttpUnit, chociaż prawdopodobnie zakończysz pisanie testów automatycznych, które są bardziej "testami integracyjnymi" (modułu) niż "testami jednostkowymi" (jednej klasy).

+0

Cóż, bardziej chodzi o testowanie jednostek, ponieważ w miarę możliwości zastąpiłbym całą interakcję między klasą serwletu z innymi klasami poprzez interakcję z mockami. – gizmo

+2

Wygląda na to, że HttpUnit nie miało żadnych zmian od 2008 roku, co sugeruje, że jest to martwy projekt. – Raedwald

+3

Czy jest nowszy zamiennik dla HttpUnit? – oconnor0

6

Czy wywołujesz metody doPost i doGet ręcznie w testach jednostkowych? Jeśli tak, możesz przesłonić metody HttpServletRequest, aby dostarczyć fałszywe obiekty.

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

    ... 
} 

HttpServletRequestWrapper to wygodna klasa Java. Proponuję, aby utworzyć metodę narzędzia w swoich testów jednostkowych do tworzenia makiety żądań http:

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

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

To nawet lepiej umieścić mock metod stworzenie w nadklasie baza serwletów i zrobić wszystkie testy serwlety jednostkowych, aby go przedłużyć.

+3

HttpServletRequestWrapper nie ma domyślnego konstruktora, tylko jeden z parametrem HttpServletRequest. –

43

W większości przypadków testuję serwlety i JSP za pomocą "testów integracyjnych" zamiast czystych testów jednostkowych. Istnieje duża liczba dodatków dla JUnit/TestNG dostępny w tym:

  • HttpUnit (najstarszy i najbardziej znany, bardzo niski poziom, który może być dobre lub złe w zależności od potrzeb)
  • HtmlUnit (wyższa poziom niż HttpUnit, co jest lepsze dla wielu projektów)
  • JWebUnit (siedzi na wierzchu innych narzędzi badawczych i próbuje je uprościć - jedna wolę)
  • WatiJ i selen (użyć przeglądarki, aby zrobić badania, które jest bardziej ciężki, ale realistyczny)

To jest test JWebUnit dla prostej procedury przetwarzania zamówień, która przetwarza dane wejściowe z formularza "orderEntry.html". Spodziewa się identyfikator klienta, nazwę klienta i jeden lub więcej elementów zamówienia:

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) może to zrobić. Zapewnia fałszywy kontener J2EE, który może być używany do testowania Serwletów. Może być również używany do testowania innych kodów po stronie serwera, takich jak EJB, JDBC, JMS, Struts. Użyłem tylko funkcji JDBC i EJB.

+2

Firma mockrunner nie była aktualizowana od 2009 roku. Czy istnieje alternatywa, która jest utrzymywana? – datguy

10

Przyjrzałem się opublikowanym odpowiedziom i pomyślałem, że zamieściłbym bardziej kompletne rozwiązanie, które faktycznie demonstruje, jak wykonać testowanie za pomocą wbudowanego GlassFish i jego wtyczki Apache Maven.

pisałem cały proces się na moim blogu Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x i umieścił kompletny projekt do pobrania na Bitbucket tutaj: image-servlet

szukałem na inne stanowisko w sprawie serwletu obrazu dla JSP/JSF tagów tuż przed Widziałem to pytanie.Więc połączyłem rozwiązanie, którego użyłem z innego postu, z kompletną wersją testowaną dla tego posta.

Jak testować

Apache Maven ma dobrze zdefiniowany cykl, który zawiera test. Użyję tego razem z innym cyklem życia o nazwie integration-test do wdrożenia mojego rozwiązania.

  1. Wyłącz standardowe testowanie jednostki cyklu życia w pluginie "surefire".
  2. Dodaj integration-test jako część egzekucji wtyczki surefire
  3. Dodaj wtyczkę GlassFish Maven do POM.
  4. Konfigurowanie GlassFish do wykonania w cyklu życia integration-test.
  5. Wykonaj testy jednostkowe (testy integracyjne).

GlassFish Plugin

Dodaj ten plugin jako część <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

Dodaj/modyfikować wtyczki jako część <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

Dodaj testy integracyjne jak na poniższym przykładzie.

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

pisałem cały proces się na moim blogu Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x i umieścił kompletny projekt do pobrania na Bitbucket tutaj: image-servlet

Jeśli masz jakieś pytania, proszę zostawić komentarz. Myślę, że jest to jeden kompletny przykład, który można wykorzystać jako podstawę każdego testowania, które planujecie dla serwletów.

0

Zaktualizowany luty 2018: OpenBrace Limited has closed down, a jego produkt ObMimic nie jest już obsługiwany.

Innym rozwiązaniem jest użycie mojej biblioteki ObMimic, która została specjalnie zaprojektowana do testowania jednostek serwletów. Zapewnia kompletne implementacje Javy wszystkich klas Servlet API i może je konfigurować i sprawdzać w razie potrzeby.

Można go używać do bezpośredniego wywoływania metod doGet/doPost z testów JUnit lub TestNG oraz do testowania dowolnych metod wewnętrznych, nawet jeśli odnoszą się one do ServletContext lub używają parametrów sesji (lub innych funkcji Servlet API).

Nie wymaga to zewnętrznego lub osadzonego pojemnika, nie ogranicza się do szerszych, opartych na protokole HTTP "testów" integracyjnych, w przeciwieństwie do makiet ogólnych, ma pełne zachowanie API Servlet "zapakowane", więc Testy mogą być oparte na "stanie" zamiast "interakcji" (np. twoje testy nie muszą opierać się na dokładnej sekwencji wywołań API Servlet wykonanych przez twój kod, ani na własnych oczekiwaniach, jak interfejs API Servlet odpowie do każdego połączenia).

Oto prosty przykład w mojej odpowiedzi na How to test my servlet using JUnit.Aby uzyskać pełne informacje i pobrać bezpłatnie, odwiedź stronę internetową ObMimic.

+0

"Aby zainstalować ObMimic: rozpakuj archiwum ObMimic-1.1.000.zip do lokalizacji, w której ..." Czekaj, co ?! – Innokenty

3

Ta implementacja testów JUnit dla servlet doPost() metoda opiera się tylko na bibliotece Mockito dla wzornikiem wystąpień HttpRequest, HttpResponse, HttpSession, ServletResponse i RequestDispatcher. Zastąp klucze parametrów i instancję JavaBean tymi, które odpowiadają wartościom przywoływanym w powiązanym pliku JSP, z którego wywoływana jest metoda doPost(). Zależność

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

'verify (session) .setAttribute (" field "," value ")' jest również prawdopodobnie dobrą asercją. – Mahdi

Powiązane problemy