1. 개요

이 빠른 사용방법(예제)에서는 HttpServletRequest 개체 를 조롱하는 몇 가지 방법을 살펴보겠습니다 .

먼저 Spring Test 라이브러리의 MockHttpServletRequest 라는 완전히 기능적인 모의 유형으로 시작합니다. 그런 다음 Mockito 및 JMockit이라는 두 가지 인기 있는 조롱 라이브러리를 사용하여 테스트하는 방법을 살펴보겠습니다. 마지막으로 익명의 하위 클래스를 사용하여 테스트하는 방법을 살펴보겠습니다.

2. HttpServletRequest 테스트

HttpServletRequest 와 같은 클라이언트 요청 정보를 조롱하려는 경우 서블릿 테스트 가 까다로울 수 있습니다 . 또한 이 인터페이스는 다양한 메서드를 정의하며 이러한 메서드를 조롱하는 데 사용할 수 있는 다양한 접근 방식이 있습니다.

테스트하려는 대상 UserServlet 클래스를 살펴보겠습니다 .

public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String firstName = request.getParameter("firstName");
        String lastName = request.getParameter("lastName");

        response.getWriter().append("Full Name: " + firstName + " " + lastName);
    }
}

doGet() 메서드 를 단위 테스트하려면 실제 런타임 동작을 시뮬레이트하기 위해 요청응답 매개 변수를 모의 처리해야 합니다.

3. Spring에서 MockHttpServletRequest 사용하기

Spring-Test 라이브러리는 HttpServletRequest 인터페이스 를 구현 하는 완전히 기능적인 클래스 MockHttpServletRequest 를 제공합니다.

이 라이브러리는 주로 Spring 애플리케이션 테스트를 목표로 하지만 Spring 관련 기능을 구현하지 않고도 MockHttpServletRequest 클래스를 사용할 수 있습니다 . 즉, 애플리케이션이 Spring을 사용하지 않더라도 우리는 여전히 HttpServletRequest 객체 를 모의하기 위해 이 의존성을 가질 수 있습니다 .

이 의존성 을 pom.xml 에 추가해 보겠습니다 .

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.20</version>
    <scope>test</scope>
</dependency>

이제 이 클래스를 사용하여 UserServlet 을 테스트하는 방법을 살펴보겠습니다 .

@Test
void givenHttpServletRequest_whenUsingMockHttpServletRequest_thenReturnsParameterValues() throws IOException {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("firstName", "Spring");
    request.setParameter("lastName", "Test");
    MockHttpServletResponse response = new MockHttpServletResponse();

    servlet.doGet(request, response);

    assertThat(response.getContentAsString()).isEqualTo("Full Name: Spring Test");
}

여기에서 실제 조롱이 관련되어 있지 않음을 알 수 있습니다. 완전한 기능을 갖춘 요청 및 응답 개체를 사용했으며 단 몇 줄의 코드로 대상 클래스를 테스트했습니다. 결과적으로 테스트 코드는 깨끗하고 읽기 쉽고 유지 관리가 가능합니다. 

4. 모의 프레임워크 사용

또는 모의 프레임워크 는 원본 개체의 런타임 동작을 모방하는 모의 개체 를 테스트하기 위한 깨끗하고 간단한 API를 제공 합니다 .

그들의 강점 중 일부는 표현 가능성과 정적개인 메서드를 모의하는 즉시 사용 가능한 기능입니다. 또한 모킹에 필요한 대부분의 상용구 코드를 피하고(사용자 지정 구현과 비교하여) 대신 테스트에 집중할 수 있습니다.

4.1. 모키토 사용

Mockito 는 내부적으로 Java Reflection API를 사용하여 목 객체를 생성하는 인기 있는 오픈 소스 테스트 자동화 프레임워크입니다.

pom.xml 에 mockito-core 의존성추가하여 시작하겠습니다 .

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.4.0</version>
    <scope>test</scope>
</dependency>

다음으로 HttpServletRequest 개체 에서 getParameter() 메서드를 조롱하는 방법을 살펴보겠습니다 .

@Test
void givenHttpServletRequest_whenMockedWithMockito_thenReturnsParameterValues() throws IOException {
    // mock HttpServletRequest & HttpServletResponse
    HttpServletRequest request = mock(HttpServletRequest.class);
    HttpServletResponse response = mock(HttpServletResponse.class);

    // mock the returned value of request.getParameterMap()
    when(request.getParameter("firstName")).thenReturn("Mockito");
    when(request.getParameter("lastName")).thenReturn("Test");
    when(response.getWriter()).thenReturn(new PrintWriter(writer));

    servlet.doGet(request, response);

    assertThat(writer.toString()).isEqualTo("Full Name: Mockito Test");
}

4.2. JMockit 사용

JMockit 은 유용한 기록 및 확인 구문을 제공하는 모의 API입니다( JUnit  및  TestNG 모두에 사용할 수 있음). Java EE 및 Spring 기반 앱을 위한 컨테이너 외부 통합 테스트 라이브러리입니다. JMockit을 사용하여 HttpServletRequest 를 조롱하는 방법을 살펴보겠습니다 .

먼저 프로젝트 에 jmockit  의존성 을 추가합니다.

<dependency> 
    <groupId>org.jmockit</groupId> 
    <artifactId>jmockit</artifactId> 
    <version>1.49</version>
    <scope>test</scope>
</dependency>

다음으로 테스트 클래스에서 모의 ​​구현을 진행해 보겠습니다.

@Mocked
HttpServletRequest mockRequest;
@Mocked
HttpServletResponse mockResponse;

@Test
void givenHttpServletRequest_whenMockedWithJMockit_thenReturnsParameterValues() throws IOException {
    new Expectations() {{
        mockRequest.getParameter("firstName"); result = "JMockit";
        mockRequest.getParameter("lastName"); result = "Test";
        mockResponse.getWriter(); result = new PrintWriter(writer);
    }};

    servlet.doGet(mockRequest, mockResponse);

    assertThat(writer.toString()).isEqualTo("Full Name: JMockit Test");
}

위에서 볼 수 있듯이 몇 줄의 설정만으로 모의 HttpServletRequest 개체로 대상 클래스를 성공적으로 테스트했습니다.

따라서 모킹 프레임워크는 우리에게 많은 수고를 덜어주고 단위 테스트를 훨씬 빠르게 작성할 수 있게 해줍니다. 반대로 Mock Object를 사용하기 위해서는 Mock API에 대한 이해가 필요하며, 보통 별도의 Framework이 필요합니다.

5. 익명 서브클래스 사용

일부 프로젝트에는 의존성 제약이 있거나 자체 테스트 클래스 구현에 대한 직접 제어를 선호할 수 있습니다. 특히 이것은 사용자 정의 구현의 재사용성이 중요한 더 큰 서블릿 코드 기반의 경우에 유용할 수 있습니다. 이러한 경우 익명 클래스가 유용합니다.

익명 클래스 는 이름이 없는 내부 클래스입니다 . 또한 실제 개체에 대한 직접적인 제어를 신속하게 구현하고 제공합니다. 테스트에 대한 추가 의존성을 포함하지 않으려는 경우 이 접근 방식을 고려할 수 있습니다.

이제 HttpServletRequest 인터페이스 를 구현하는 익명 하위 클래스를 만들고 이를 사용하여 doGet() 메서드 를 테스트해 보겠습니다.

public static HttpServletRequest getRequest(Map<String, String[]> params) {
    return new HttpServletRequest() {
        public Map<String, String[]> getParameterMap() {
            return params;
        }

        public String getParameter(String name) {
            String[] values = params.get(name);
            if (values == null || values.length == 0) {
                return null;
            }
            return values[0];
        }

        // More methods to implement
    }
};

다음으로 이 요청을 테스트 중인 클래스에 전달해 보겠습니다.

@Test
void givenHttpServletRequest_whenUsingAnonymousClass_thenReturnsParameterValues() throws IOException {
    final Map<String, String[]> params = new HashMap<>();
    params.put("firstName", new String[] { "Anonymous Class" });
    params.put("lastName", new String[] { "Test" });

    servlet.doGet(getRequest(params), getResponse(writer));

    assertThat(writer.toString()).isEqualTo("Full Name: Anonymous Class Test");
}

이 솔루션의 단점은 모든 추상 메서드에 대한 더미 구현으로 익명 클래스를 만들어야 한다는 것입니다. 또한 HttpSession 과 같은 중첩 개체에  특정 구현이 필요할 수 있습니다 .

6. 결론

이 기사에서는 서블릿에 대한 단위 테스트를 작성할 때 HttpServletRequest 객체 를 조롱하는 몇 가지 옵션에 대해 논의했습니다 . 모킹 프레임워크를 사용하는 것 외에도 MockHttpServletRequest 클래스를 사용한 테스트가 사용자 정의 구현보다 더 깨끗하고 효율적인 것으로 보입니다.

항상 그렇듯이 이러한 예제의 코드는  GitHub 에서 사용할 수 있습니다 .

res – Junit (guide) (cat=Testing)