Вопрос

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

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

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

Это было полезно?

Решение

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

Другие советы

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

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

Это тест 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. Запускайте модульные тесты (интеграционные тесты).

Плагин 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>

Надежный Плагин

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

     ...
}

Тот Самый HttpServletRequestWrapper это удобный Java-класс.Я предлагаю вам создать служебный метод в ваших модульных тестах для создания макетных http-запросов:

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

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

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

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

Эта реализация метода JUnit test для сервлета 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);
    }
}

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top