2008-09-18 2 views
51

Я хотел бы знать, что было бы лучшим способом выполнить модульное тестирование сервлета.Модульное тестирование сервлета Java

Тестирование внутренних методов не является проблемой, если они не относятся к контексту сервлета, но как насчет тестирования методов doGet/doPost, а также внутреннего метода, который ссылается на контекст или использует параметры сеанса ?

Есть ли способ сделать это, просто используя классические инструменты, такие как JUnit или, желательно, TestNG? Мне нужно встроить сервер tomcat или что-то в этом роде?

+0

возможно дубликат [Unit-тестирования сервлетов] (http://stackoverflow.com/questions/53532/unit-testing- сервлеты) – Raedwald 2013-06-10 11:51:50

ответ

12

Попробуйте HttpUnit, хотя вы, вероятно, закончите писать автоматические тесты, которые являются более «интеграционными тестами» (модуля), чем «модульные тесты» (одного класса).

+0

Ну, это действительно больше об Unit Testing, поскольку я, по возможности, заменил бы все взаимодействие между классами сервлетов другими классами путем взаимодействия с mocks. – gizmo 2008-09-18 08:25:36

+2

HttpUnit, похоже, не изменился с 2008 года, предполагая, что это мертвый проект. – Raedwald 2013-06-09 18:30:00

+3

Есть ли более новая замена HttpUnit? – oconnor0 2013-06-11 17:36:52

6

Вы вызываете методы doPost и doGet вручную в модульных тестах? Если это так, вы можете переопределить методы HttpServletRequest для предоставления макетных объектов.

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

    ... 
} 

The HttpServletRequestWrapper является удобство Java класса. Я предлагаю вам создать метод полезности в модульных тестах, чтобы создать ложные запросы HTTP:

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

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

Это даже лучше поставить методы имитировали создание в базе сервлет суперкласса и сделать все тесты сервлетов единицы, чтобы продлить его.

43

Большую часть времени я тестирую сервлеты и JSP через «Интеграционные тесты», а не чистые тесты устройства. Есть большое количество дополнений для JUnit/TestNG доступны в том числе:

  • HttpUnit (самый старый и самый известный, очень низкий уровень, который может быть хорошим или плохим в зависимости от ваших потребностей)
  • HtmlUnit (выше уровня, чем HttpUnit, что лучше для многих проектов)
  • JWebUnit (сидит на вершине других инструментов тестирования и пытается упростить их - тот, который я предпочитаю)
  • WatiJ и Селен (используйте свой браузер, чтобы сделать тестирование, которое более тяжеловесный, но реалистичный)

Это тест JWebUnit для простого сервлета обработки заказов, который обрабатывает ввод из формы 'orderEntry.html'. Он ожидает идентификатор клиента, имя клиента и один или несколько элементов заказа:

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) может сделать это. Он предоставляет макет J2EE-контейнера, который можно использовать для тестирования Servlets. Его также можно использовать для тестирования другого кода на стороне сервера, такого как EJB, JDBC, JMS, Struts. Я сам использовал возможности JDBC и EJB.

10

Я посмотрел на опубликованные ответы и подумал, что отправлю более полное решение, которое на самом деле демонстрирует, как проводить тестирование с использованием встроенного GlassFish и его плагина Apache Maven.

Я написал полный процесс на моем блоге Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x и разместил полный проект для загрузки на Bitbucket здесь: image-servlet

Я смотрел на другую должность на качестве сервлета изображения для JSP/JSF тегов, прежде чем я увидел это вопрос.Поэтому я объединил решение, которое я использовал с другого поста, с полной версией, проверенной модулем для этого сообщения.

Как проверить

Apache Maven имеет четко определенный жизненный цикл, который включает в себя test. Я буду использовать его вместе с другим жизненным циклом, который называется integration-test для реализации моего решения.

  1. Отключить стандартное тестирование жизненного цикла в плагине surefire.
  2. Добавить integration-test в качестве части исполнения верфи-плагина
  3. Добавить плагин GlassFish Maven к POM.
  4. Настройте GlassFish для выполнения во время жизненного цикла integration-test.
  5. Пробные испытания (интеграционные тесты).

GlassFish Плагин

Добавьте этот плагин как часть <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 Плагин

Добавить/изменить плагин как часть <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

Добавить интеграционные тесты, как на примере ниже.

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

Я написал полный процесс на моем блоге Using GlassFish 3.1.1 Embedded with JUnit 4.x and HtmlUnit 2.x и разместил полный проект для загрузки на Bitbucket здесь: image-servlet

Если у вас есть какие-либо вопросы, пожалуйста, оставьте комментарий. Я думаю, что это один полный пример использования вами в качестве основы для любого тестирования, которое вы планируете использовать для сервлетов.

0

Обновлено фев 2018: OpenBrace Limited has closed down, а его продукт ObMimic больше не поддерживается.

Другое решение - использовать мою библиотеку ObMimic, которая специально предназначена для модульного тестирования сервлетов. Он предоставляет полные простые Java-реализации всех классов Servlet API, и вы можете их настроить и проверить, если это необходимо для ваших тестов.

Вы действительно можете использовать его для прямого вызова методов doGet/doPost из тестов JUnit или TestNG и для проверки любых внутренних методов, даже если они относятся к ServletContext или используют параметры сеанса (или любые другие функции API сервлета).

Это не требует внешнего или встроенного контейнера, не ограничивает вас более широкими тестами «интеграции» на основе HTTP, и, в отличие от мошенников общего назначения, он имеет полное поведение API Servlet, «испеченное», поэтому ваш тесты могут быть основаны на «состоянии», а не на «взаимодействии» (например, ваши тесты не должны полагаться на точную последовательность вызовов API Servlet, выполненных вашим кодом, а также на ваши собственные ожидания от того, как API Servlet будет отвечать для каждого вызова).

В моем ответе How to test my servlet using JUnit есть простой пример.Для получения полной информации и бесплатной загрузки см. Сайт ObMimic.

3

Эта реализация теста JUnit для сервлета doPost() метод основан только на библиотеке Mockito для насмешливый до экземпляров HttpRequest, HttpResponse, HttpSession, ServletResponse и RequestDispatcher. Замените ключи параметров и экземпляр JavaBean теми, которые соответствуют значениям, указанным в связанном файле JSP, из которого вызывается doPost().

Mockito Maven зависимость:

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

тест 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); 
    } 
} 
Смежные вопросы