Jersey Test + MockitoでJAX-RSリソースクラスの単体テスト
Jersey TestでJAX-RSリソースクラスの単体テストするときに、Mockitoでモックのビジネスロジックを差し込みます。
環境
- Payara Web ML 4.1.1.161
- Jersey 2.22.1
pom.xml
- JUnit
- Jersey Test
- Jersey Testの実行環境
- Mockito
が必要です。
jersey-test-framework-provider-grizzly2を依存性に含めると、Jersey Testと実行環境が両方入ります。
<properties> <jersey.version>2.22.1</jersey.version> <jee-webapi.version>7.0</jee-webapi.version> </properties> <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> <dependencies> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>${jee-webapi.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId> <artifactId>jersey-test-framework-provider-grizzly2</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>1.10.19</version> <scope>test</scope> </dependency> </dependencies>
テスト対象のリソースクラスなど
package sample; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/api") public class MyApplication extends Application { // 中身は空 }
package sample; import java.util.List; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import javax.ws.rs.core.MediaType; @Path("/hello") @Produces(value = MediaType.APPLICATION_JSON) @RequestScoped public class HelloResource { // テスト時はここをモックに差し替える @Inject private HelloLogic helloLogic; @GET public Response getAll() throws Exception { List<HelloDto> list = helloLogic.selectAll(); return Response.ok(list).build(); } }
package sample; public class HelloDto { private String name; // setter/getter/コンストラクタ省略 }
package sample; import java.util.List; import javax.enterprise.context.RequestScoped; @RequestScoped public class HelloLogic { public List<HelloDto> selectAll() { // 本来は何らかの複雑な処理があると考えてください return Arrays.asList(new HelloDto("AAA"), new HelloDto("BBB"), new HelloDto("CCC")); } }
テストクラスの作成
package sample; import org.glassfish.hk2.api.Factory; import org.glassfish.hk2.utilities.Binder; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Test; import javax.ws.rs.core.Application; import javax.ws.rs.core.GenericType; import java.util.Arrays; import java.util.List; import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; import static org.mockito.Mockito.*; public class HelloResourceTest extends JerseyTest { /** * HelloLogicのモックのファクトリークラス */ private static class MockHelloLogicFactory implements Factory<HelloLogic> { @Override public HelloLogic provide() { // mock() / when() / thenReturn() はMockitoのメソッド HelloLogic mockLogic = mock(HelloLogic.class); when(mockLogic.selectAll()).thenReturn(Arrays.asList( new HelloDto("仮のA"), new HelloDto("仮のB") )); return mockLogic; } @Override public void dispose(ProductQueryDao instance) { // 何もしない } } @Override protected Application configure() { Binder binder = new AbstractBinder() { @Override protected void configure() { // bindFactory() + to() でモックに差し替え bindFactory(MockHelloLogicFactory.class) .to(HelloLogic.class); } }; return new ResourceConfig() .packages(true, MyApplication.class.getPackage().getName()) .register(binder); } @Test public void 全件取得でき件数が2件() { List<HelloDto> list = target("hello") .request() .get(new GenericType<List<HelloDto>>(){}); assertThat(list.size(), is(2)); } }
ポイントは、ファクトリークラスを作成することと、bindFactory()
メソッドを利用すること。
下記の@backpaper0さんの資料にあるbind()
メソッドは、Javadocに
Does NOT bind the service type itself as a contract type.
とあり、Mockitoで作ったmockLogic
を引数に指定すると、例外でテストがこけた。
bind()
の引数はインタフェースのみ指定可能なのかも?
参考資料
java - Mocking EJB's with Mockito and Jersey Test Framework - Stack Overflow
http://backpaper0.github.io/ghosts/jaxrs-getting-started-and-practice.html#/12/8