はじめに
この記事は リクルートライフスタイル Advent Calendar 2015
リクルートライフスタイル Advent Calendar 2015 - Qiita の9日目です。
ホットペッパービューティーで開発を担当している満澤です。
現在、Java 8 + Spring Boot 1.3.0 + MyBatis 3.2.8でRestfulな新規APIを開発しています。
Spring Bootは、小・中規模な新規サービスを高いスピード感で、
開発していくのに適しているんじゃないかと感じています。
現実、まわりでもそういった案件がどんどん増えてます。
その際、当然、新たにドキュメントの整備をしていかなければならないのですが、
ここで、最近新しい技術をSpringが提供し始めました。
その名もSpring REST Docs。
何ができるかというと、
Spring MVC Test を実行した結果をスニペット(Asciidoc)として出力できます。
自分で手書きしたドキュメント(Asciidoc)とスニペットを組み合わせられます。
さっそく、どんなドキュメントが出力されるか試してみます。
環境
- Java 1.8.0_45
- Maven 3.2.1
- Spring Boot 1.3.0
- Spring REST Docs 1.0.0
サンプル
Spring BootのREST APIにREST Docsでドキュメント生成するサンプルです。
※Spring Bootの実装については色んなところで記事がでているので割愛
<!-- REST Docsに関する記載を抜粋 -->
<properties>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Documentation.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.2</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<attributes>
<snippets>${snippetsDirectory}</snippets>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.outputDirectory}/static/docs
</outputDirectory>
<resources>
<resource>
<directory>
${project.build.directory}/generated-docs
</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>1.0.0.RELEASE</version>
<scope>test</scope>
</dependency>
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ApplicationContext.class)
@WebAppConfiguration
public class RestDocsExampleTest {
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
}
@Test
public void testRequestMapping() throws Exception {
this.mockMvc.perform(get("/api/v1/employee/{year}?employeId=E000000001", "2015")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("controller-doc",
pathParameters(
parameterWithName("year").description("入社年:必須、yyyy形式")),
requestParameters(
parameterWithName("employeId").description("従業員ID:任意、半角英数字10桁"))
));
}
ポイントはMockMvc.andDo(document("controller-doc", ・・・)でドキュメントが出力されるところ、
pathParameters()、requestParameters()でドキュメントに出力する内容が記載できます。
上記の例ではコントローラクラスをテスト対象にして、
URLパラメータについて、ドキュメントに出力するようにしていますが、
これ以外にも、レスポンスやヘッダの中身など様々な記載が可能です。
まとめ
簡単です、とてもシンプル。
ドキュメントの質も、一定のラインは満たせているでしょうし、(Asciidocなので、リッチにカスタマイズも可能)
何より、ドキュメント作成のコストをテストコードにかけれるという仕組みがとても好印象をもちました。
ドキュメントが置いてけぼりというのはよく聞く話ですが、
テストが成功した時にドキュメントが生成されるので、最新の状態を保ちやすい、
CIと組み合わせて色んなことできるでしょうし、Jarなどに含めて一緒に配布することも容易です。
実際のプロダクトに適用させるには、詰めるところはありそうですが、
Springで開発しているのであれば、導入を検討してもいいのではないでしょうか。
参考
http://docs.spring.io/spring-restdocs/docs/1.0.0.RELEASE/reference/html5/