Модульное тестирование сервлета Java - PullRequest
52 голосов
/ 18 сентября 2008

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

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

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

Ответы [ 7 ]

44 голосов
/ 18 сентября 2008

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

  • HttpUnit (самый старый и самый известный, очень низкий уровень, который может быть хорошим или плохим в зависимости от ваших потребностей)
  • HtmlUnit (более высокий уровень, чем HttpUnit, что лучше для многих проектов)
  • JWebUnit (сидит поверх других инструментов тестирования и пытается упростить их - тот, который я предпочитаю)
  • WatiJ и 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;
}
12 голосов
/ 18 сентября 2008

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

10 голосов
/ 06 января 2013

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

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

Я просматривал другой пост в сервлете изображений для тегов 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>

Плагин 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 здесь: image- сервлет

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

6 голосов
/ 18 сентября 2008

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

6 голосов
/ 18 сентября 2008

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

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

3 голосов
/ 26 июля 2016

Эта реализация теста 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 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);
    }
}
0 голосов
/ 15 августа 2013

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...