자바 서블릿 단위 테스트
서블릿의 단위 테스트를 수행하는 가장 좋은 방법이 무엇인지 알고 싶습니다.
내부 메소드를 테스트하는 것은 서블릿 컨텍스트를 참조하지 않는 한 문제가 되지 않지만 doGet/doPost 메소드와 컨텍스트를 참조하거나 세션 매개변수를 사용하는 내부 메소드를 테스트하는 것은 어떻습니까?
JUnit 또는 가급적이면 TestNG와 같은 고전적인 도구를 사용하여 간단히 이 작업을 수행할 수 있는 방법이 있습니까? Tomcat 서버 또는 이와 유사한 것을 포함해야 했습니까?
(단일 클래스의) '단위 테스트'보다 더 많은 '통합 테스트'(모듈의)인 자동화된 테스트를 작성하게 될 가능성이 높지만 HttpUnit을 시도하십시오 .
대부분의 경우 순수한 단위 테스트가 아닌 '통합 테스트'를 통해 서블릿과 JSP를 테스트합니다. 다음을 포함하여 사용할 수 있는 JUnit/TestNG용 애드온이 많이 있습니다.
- HttpUnit (가장 오래되고 가장 잘 알려진, 필요에 따라 좋거나 나쁠 수 있는 매우 낮은 수준)
- HtmlUnit (많은 프로젝트에 더 나은 HttpUnit보다 높은 수준)
- JWebUnit (다른 테스트 도구 위에 위치하며 이를 단순화하려고 합니다. 내가 선호하는 도구)
- WatiJ 및 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 플러그인을 사용하여 테스트를 수행하는 방법을 실제로 보여주는 보다 완전한 솔루션을 게시할 것이라고 생각했습니다.
내 블로그 Using GlassFish 3.1.1 Embedded with JUnit 4.x 및 HtmlUnit 2.x 에 전체 프로세스를 작성하고 Bitbucket에서 다운로드할 전체 프로젝트를 여기: image-servlet에 배치했습니다.
이 질문을 보기 직전에 JSP/JSF 태그용 이미지 서블릿에 대한 다른 게시물을 보고 있었습니다. 그래서 다른 게시물에서 사용한 솔루션을 이 게시물의 완전한 단위 테스트 버전과 결합했습니다.
테스트 방법
Apache Maven에는 다음을 포함하는 잘 정의된 수명 주기가 있습니다 test
. integration-test
내 솔루션을 구현하기 위해 호출 되는 다른 수명 주기와 함께 이것을 사용할 것 입니다.
- Surefire 플러그인에서 표준 수명 주기 단위 테스트를 비활성화합니다.
integration-test
Surefire-plugin 실행의 일부로 추가- POM에 GlassFish Maven 플러그인을 추가합니다.
integration-test
수명 주기 동안 실행되도록 GlassFish를 구성합니다 .- 단위 테스트(통합 테스트)를 실행합니다.
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
아래 예시와 같은 통합 테스트를 추가합니다.
@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 및 HtmlUnit 2.x 에 전체 프로세스를 작성하고 Bitbucket에서 다운로드할 전체 프로젝트를 여기: image-servlet에 배치했습니다.
질문이 있으시면 댓글을 남겨주세요. 나는 이것이 서블릿에 대해 계획하고 있는 모든 테스트의 기초로 사용할 하나의 완전한 예라고 생각합니다.
단위 테스트에서 수동으로 doPost 및 doGet 메서드를 호출하고 있습니까? 그렇다면 HttpServletRequest 메소드를 재정의하여 모의 객체를 제공할 수 있습니다.
myServlet.doGet(new HttpServletRequestWrapper() {
public HttpSession getSession() {
return mockSession;
}
...
}
HttpServletRequestWrapper는 편의 자바 클래스입니다. 모의 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 컨테이너를 제공합니다. 또한 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 웹사이트를 참조하십시오 .
참조URL : https://stackoverflow.com/questions/90907/unit-testing-a-java-servlet
'IT이야기' 카테고리의 다른 글
multiprocessing.Process를 사용하여 최대 동시 프로세스 수 (0) | 2021.10.02 |
---|---|
Stateless 위젯 클래스의 키 (0) | 2021.10.02 |
SQL Server의 다른 저장 프로시저에서 호출된 저장 프로시저의 SELECT 출력을 억제하는 방법 (0) | 2021.10.01 |
XHTML 역할 속성이란 (0) | 2021.10.01 |
바이너리 패치 생성 방법 (0) | 2021.10.01 |