문제

서블릿의 단위 테스트를 수행하는 가장 좋은 방법이 무엇인지 알고 싶습니다.

내부 메소드를 테스트하는 것은 서블릿 컨텍스트를 참조하지 않는 한 문제가 되지 않습니다. 하지만 doGet/doPost 메소드뿐만 아니라 컨텍스트를 참조하거나 세션 매개변수를 사용하는 내부 메소드를 테스트하는 것은 어떻습니까?

JUnit이나 가급적이면 TestNG와 같은 기존 도구를 사용하여 이를 수행할 수 있는 방법이 있습니까?Tomcat 서버 같은 것을 내장해야 했나요?

도움이 되었습니까?

해결책

노력하다 HttpUnit, 비록 단일 클래스의 '단위 테스트'보다는 모듈의 '통합 테스트'에 더 가까운 자동화된 테스트를 작성하게 될 가능성이 높습니다.

다른 팁

대부분의 경우 순수한 단위 테스트보다는 '통합 테스트'를 통해 서블릿과 JSP를 테스트합니다.다음을 포함하여 사용 가능한 JUnit/TestNG용 추가 기능이 많이 있습니다.

  • HttpUnit (가장 오래되고 가장 잘 알려진 매우 낮은 수준으로 필요에 따라 좋거나 나쁠 수 있음)
  • HtmlUnit (많은 프로젝트에 더 나은 HttpUnit보다 높은 수준)
  • JWebUnit (다른 테스트 도구 위에 위치하여 이를 단순화하려고 시도합니다 - 제가 선호하는 도구입니다)
  • 와티제이 및 Selenium(브라우저를 사용하여 테스트를 수행하세요. 이는 더 무겁지만 현실적입니다.)

이것은 'orderEntry.html' 형식의 입력을 처리하는 간단한 주문 처리 서블릿에 대한 JWebUnit 테스트입니다.고객 ID, 고객 이름 및 하나 이상의 주문 항목이 필요합니다.

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 플러그인을 사용하여 테스트를 수행하는 방법을 실제로 보여주는 더 완전한 솔루션을 게시해야겠다고 생각했습니다.

블로그에 전체 과정을 올렸어요 JUnit 4.x 및 HtmlUnit 2.x와 함께 GlassFish 3.1.1 Embedded 사용 Bitbucket에서 다운로드할 전체 프로젝트를 여기에 배치했습니다. 이미지 서블릿

이 질문을 보기 직전에 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>

확실한 플러그인

플러그인을 다음의 일부로 추가/수정하세요. <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>

HTML단위

아래 예시와 같이 통합 테스트를 추가하세요.

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

블로그에 전체 과정을 올렸어요 JUnit 4.x 및 HtmlUnit 2.x와 함께 GlassFish 3.1.1 Embedded 사용 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 컨테이너를 제공합니다.또한 EJB, JDBC, JMS, Struts와 같은 다른 서버 측 코드를 단위 테스트하는 데 사용할 수도 있습니다.나는 JDBC와 EJB 기능만을 사용해왔습니다.

서블릿 doPost() 메소드에 대한 JUnit 테스트 구현은 인스턴스를 모형화하기 위해 Mockito 라이브러리에만 의존합니다. HttpRequest, HttpResponse, HttpSession, ServletResponse 그리고 RequestDispatcher.매개변수 키와 JavaBean 인스턴스를 doPost()가 호출되는 연관된 JSP 파일에서 참조되는 값에 해당하는 키로 바꾸십시오.

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년 2월 업데이트됨: OpenBrace Limited가 폐쇄되었습니다, ObMimic 제품은 더 이상 지원되지 않습니다.

또 다른 해결책은 내 ObMimic 서블릿의 단위 테스트를 위해 특별히 설계된 라이브러리입니다.이는 모든 Servlet API 클래스의 완전한 일반 Java 구현을 제공하며 테스트에 필요에 따라 이를 구성하고 검사할 수 있습니다.

실제로 이를 사용하여 JUnit 또는 TestNG 테스트에서 doGet/doPost 메소드를 직접 호출하고, ServletContext를 참조하거나 세션 매개변수(또는 기타 Servlet API 기능)를 사용하더라도 내부 메소드를 테스트할 수 있습니다.

이는 외부 또는 내장된 컨테이너가 필요하지 않으며 광범위한 HTTP 기반 "통합" 테스트로 제한하지 않으며 범용 모의 테스트와는 달리 전체 Servlet API 동작이 "구워져" 있으므로 테스트를 " "상호작용" 기반이 아닌 상태" 기반(예:테스트는 코드에서 수행된 Servlet API 호출의 정확한 순서나 Servlet API가 각 호출에 어떻게 응답할지에 대한 사용자 자신의 기대에 의존할 필요가 없습니다.

내 대답에는 간단한 예가 있습니다. JUnit을 사용하여 서블릿을 테스트하는 방법.자세한 내용과 무료 다운로드를 보려면 다음을 참조하세요. ObMimic 웹사이트.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top