1. 概要

このクイックチュートリアルでは、HttpServletRequestオブジェクトをモックするいくつかの方法を見ていきます

まず、完全に機能するモックタイプ(Springテストライブラリの MockHttpServletRequest )から始めます。 次に、2つの人気のあるモックライブラリである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()メソッドの単体テストを行うには、requestパラメーターとresponseパラメーターの両方をモックして、実際の実行時の動作をシミュレートする必要があります。

3. SpringからMockHttpServletRequestを使用する

Spring-Test ライブラリは、HttpServletRequestインターフェイスを実装する完全に機能するクラスMockHttpServletRequestを提供します。

このライブラリは主にSpringアプリケーションのテストを目的としていますが、 Spring固有の機能を実装せずに、そのMockHttpServletRequestクラスを使用できます。 つまり、アプリケーションがSpringを使用しない場合でも、HttpServletRequestオブジェクトをモックするためだけにこの依存関係を持つことができます。

このdependencyを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を提供します。

それらの長所のいくつかは、その表現力と、staticおよびprivateメソッドをモックするすぐに使える機能です。 さらに、(カスタム実装と比較して)モックに必要なボイラープレートコードのほとんどを回避し、代わりにテストに集中することができます。

4.1. Mockitoを使用する

Mockito は、 Java ReflectionAPIを内部的に使用してモックオブジェクトを作成する人気のあるオープンソースのテスト自動化フレームワークです。

mockito-coredependentencypom.xmlに追加することから始めましょう。

<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です(JUnitTestNGの両方に使用できます)。 これは、JavaEEおよび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オブジェクトを使用してターゲットクラスを正常にテストできました。

したがって、モックフレームワークを使用すると、手間を大幅に節約でき、単体テストの記述が大幅に高速化されます。 それどころか、モックオブジェクトを使用するには、モックAPIを理解する必要があり、通常、別のフレームワークが必要です。

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から入手できます。