Edited at

Spring REST Docsでドキュメント作成

More than 3 years have passed since last update.


はじめに

この記事は リクルートライフスタイル 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の実装については色んなところで記事がでているので割愛


pom.xml

    <!-- 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>



RestDocsExampleTest.java


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パラメータについて、ドキュメントに出力するようにしていますが、

これ以外にも、レスポンスやヘッダの中身など様々な記載が可能です。

出力されたドキュメント

doc.jpg


まとめ

簡単です、とてもシンプル。

ドキュメントの質も、一定のラインは満たせているでしょうし、(Asciidocなので、リッチにカスタマイズも可能)

何より、ドキュメント作成のコストをテストコードにかけれるという仕組みがとても好印象をもちました。

ドキュメントが置いてけぼりというのはよく聞く話ですが、

テストが成功した時にドキュメントが生成されるので、最新の状態を保ちやすい、

CIと組み合わせて色んなことできるでしょうし、Jarなどに含めて一緒に配布することも容易です。

実際のプロダクトに適用させるには、詰めるところはありそうですが、

Springで開発しているのであれば、導入を検討してもいいのではないでしょうか。

参考

http://docs.spring.io/spring-restdocs/docs/1.0.0.RELEASE/reference/html5/