Модульное тестирование сервлета Java


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

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

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

7 52

7 ответов:

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

большую часть времени я тестирую сервлеты и 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;
}

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

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

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

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

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

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

Плагин Некорректно

добавить этот плагин в составе <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();
}

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

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

вы вызываете методы 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   
   }
}

еще лучше поместить макетные методы создания в базовый суперкласс сервлетов и сделать все модульные тесты сервлетов расширить его.

Mockrunner (http://mockrunner.sourceforge.net/index.html) может сделать это. Он предоставляет макет контейнера J2EE, который можно использовать для тестирования сервлетов. Его также можно использовать для модульного тестирования другого кода на стороне сервера, такого как EJBs, JDBC, JMS, Struts. Я сам использовал только возможности JDBC и EJB.

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

зависимостей Maven Mockito:

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

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

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

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

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

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

есть простой пример в моем ответе Как проверить мой сервлет с помощью JUnit. Для получения полной информации и бесплатной загрузки см. ObMimic сайт.