2016年7月2日 星期六

對 Jersey 製作的 RESTful API 做單元測試(二):啟用 Servlet

Jersey 中使用 Jersey Test Framework 做測試時
如果遇到需要使用 Servlet 的狀況,例如使用了 @Context HttpServletRequest request
可能就會注意到,HttpServletRequest 這類應該要自動被注入的東西,在測試時都沒有被注入
因此會發生像是 NullPointerException 等等的錯誤。

雖然有不少網友有去 Jersey 社群詢問或直接開 bug
但官方開發人員的回應是表示這並沒有違背設計,因此也沒有打算要修復。
至於沒有違背設計的原因,可以參考 [6],實際上預設 JerseyTest 會打開的伺服器,僅包含 HTTP container
因此對於 Servlet 注入所需要的 Servlet container 沒有任何反應。
如果想要使用 Servlet 的話,依照官方文件 [6] 的描述,應該改用 GrizzlyWebTestContainerFactory 去提供 test container。

要使用 GrizzlyWebTestContainerFactory,首先當然是 Maven 的設定必須使用 Grizzly
因此必須加上這樣的 dependancy:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<properties>
    <jersey.version>2.23</jersey.version>
</properties>

</dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.test-framework.providers</groupId>
        <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

在上述的 pom.xml 中,指定了會使用 Grizzly2 來提供 test container 服務,並且使用的版本是 2.23。

除了 Maven 以外,在程式碼的部份也需要做一些額外的設定,以要求使用具有 Servlet 功能的 container。

public class MyTest extends JerseyTest {
    @Override
    protected TestContainerFactory getTestContainerFactory() {
        return new GrizzlyWebTestContainerFactory();
    }

    @Override
    protected DeploymentContext configureDeployment() {
        ResourceConfig config = new ResourceConfig(MyRestfulApi.class);
        return ServletDeploymentContext
            .forServlet(new ServletContainer(config))
            .build();
    }
    
    @Test
    public void testServlet () {
        Response response = target("/")
            .queryParam("a", 1)
            .queryParam("b", "test")
            .request()
            .get(Response.class);
        Assert.assertEquals(200, response.getStatus());
    }
}

@Path("/")
@Produces
public class MyRestfulApi {
    @GET
    public Response test (@Context HttpServletRequest request) {
        System.out.println("Request URL: " + request.getRequestURL().toString());
        System.out.println("Queries: " + request.getQueryString());
        return Response.ok().build();
    }
}

在上述的程式碼中,有兩個類別,MyRestfulApi 是撰寫的 Restful API,而 MyTest 則是測試的程式。
在 RESTful API 中,做的事情只是單純把 Request 的網址和參數印出來而已。
而測試程式 MyTest,則是透過 Override 覆寫了兩個 method
用來指定要使用具有 Servlet 功能的 container,並且 context 也要以 Servlet 的形式佈署
這樣才能順利地讓 Servlet 功能正常運作。
接著在測試案例 testServlet() 中,就產生一個會送出兩個 query parameters 的 HTTP request 給 MyRestfulApi
並且檢查回應的 HTTP status code 是否為 200。

真正執行上述的測試程式時,console 會印出下列的結果,表示 Servlet 確實正常在運作。

Request URL: http://localhost:9998/
Queries: a=1&b=test
相關資源

上篇:對 Jersey 製作的 RESTful API 做單元測試(一)

參考資料
  1. jersey2 Unit testing,HttpServletRequest is null
  2. Problems running JerseyTest when dealing with HttpServletResponse
  3. Injection of HttpServletRequest when using JerseyTest does not work
  4. HttpServletRequest always null on test case
  5. JerseyTest using GrizzlyWebTestContainerFactory in Jersey 2.13
  6. 26.2. Supported Containers

沒有留言: