1. 概要

Spring REST Docs は、正確で読みやすいRESTfulサービスのドキュメントを生成します。 手書きのドキュメントと、Springテストで生成された自動生成されたドキュメントスニペットを組み合わせます。

2. 利点

プロジェクトの背後にある主要な哲学の1つは、テストを使用してドキュメントを作成することです。 これにより、生成されるドキュメントが常にAPIの実際の動作と正確に一致することが保証されます。 さらに、出力は、AsciiDoc構文を中心としたパブリッシングツールチェーンであるAsciidoctorで処理する準備ができています。 これは、Springフレームワークのドキュメントを生成するために使用されるのと同じツールです。

これらのアプローチは、他のフレームワークによって課せられる制限を軽減します。 Spring REST Docsは、正確で簡潔で、適切に構成されたドキュメントを作成します。 このドキュメントにより、Webサービスの利用者は最小限の手間で必要な情報を入手できます。

このツールには、次のような他の利点があります。

  • curlおよびhttpリクエストスニペットが生成されます
  • ドキュメントをプロジェクトのjarファイルに簡単にパッケージ化できます
  • スニペットに追加情報を簡単に追加できます
  • JSONとXMLの両方をサポート

スニペットを生成するテストは、Spring MVCテストサポート、SpringWebfluxのWebTestClient 、またはREST-Assuredのいずれかを使用して記述できます。

この例では、Spring MVCテストを使用しますが、他のフレームワークの使用は非常に似ています。

3. 依存関係

プロジェクトでSpringRESTDocsの使用を開始する理想的な方法は、依存関係管理システムを使用することです。 ここでは、ビルドツールとしてMavenを使用しているため、以下の依存関係をコピーしてPOMに貼り付けることができます。

<dependency>
    <groupId>org.springframework.restdocs</groupId>
    <artifactId>spring-restdocs-mockmvc</artifactId>
    <version>2.0.4.RELEASE</version>
</dependency>

MavenCentralで依存関係の新しいバージョンを確認することもできますここ

この例では、Spring MVCテストサポートを使用してテストを作成しているため、 spring-restdocs-mockmvc依存関係が必要です。

WebTestClientまたはRESTAssuredを使用してテストを作成する場合は、 spring-restdocs-webtestclientおよびspring-restdocs-restassuredの依存関係が必要です。

4. 構成

前述のように、Spring MVCテストフレームワークを使用して、文書化されるRESTサービスにリクエストを送信します。 テストを実行すると、リクエストと結果のレスポンスのドキュメントスニペットが生成されます。

ライブラリは、JUnit4とJUnit5の両方のテストで使用できます。 それぞれに必要な構成を見てみましょう。

4.1. JUnit4の構成

JUnit 4テストのドキュメントスニペットを生成する最初のステップは、JUnit@Ruleとして注釈が付けられたパブリックJUnitRestDocumentationフィールドを宣言することです。

JUnitRestDocumentation ルールは、生成されたスニペットを保存する出力ディレクトリで構成されます。 たとえば、このディレクトリはMavenのビルドアウトディレクトリにすることができます。

@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");

次に、 MockMvc コンテキストを設定して、ドキュメントを生成するように構成します。

@Autowired
private WebApplicationContext context;

private MockMvc mockMvc;

@Before
public void setUp(){
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
      .apply(documentationConfiguration(this.restDocumentation))
      .build();
}

MockMvc オブジェクトは、MockMvc RestDocumentationConfigurerを使用して構成されます。 このクラスのインスタンスは、 org.springframework.restdocs.mockmvc.MockMvcRestDocumentationの静的documentationConfiguration()メソッドから取得できます。

4.2. JUnit5の構成

JUnit 5テストを使用するには、RestDocumentationExtensionクラスでテストを拡張する必要があります。

@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
@SpringBootTest
public class ApiDocumentationJUnit5IntegrationTest { //... }

このクラスは、Mavenを使用する場合は / target / generate-snippets 出力ディレクトリで、Gradleの場合は / build /generate-snippetsで自動的に構成されます。

次に、@BeforeEachメソッドでMockMvcインスタンスを設定する必要があります。

@BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
  RestDocumentationContextProvider restDocumentation) {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
      .apply(documentationConfiguration(restDocumentation)).build();
}

テストにJUnitを使用していない場合は、ManualRestDocumentationクラスを使用する必要があります。

5. RESTfulサービス

文書化できるCRUDRESTfulサービスを作成しましょう。

@RestController
@RequestMapping("/crud")
public class CRUDController {
	
    @GetMapping
    public List<CrudInput> read(@RequestBody CrudInput crudInput) {
        List<CrudInput> returnList = new ArrayList<CrudInput>();
        returnList.add(crudInput);
        return returnList;
    }
	
    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping
    public HttpHeaders save(@RequestBody CrudInput crudInput) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setLocation(
          linkTo(CRUDController.class).slash(crudInput.getTitle()).toUri());
        return httpHeaders;
    }
	
    @DeleteMapping("/{id}")
    public void delete(@PathVariable("id") long id) {
        // delete
    }
}

次に、CRUDControllerベースエンドポイントへのリンクを含むページを返すIndexControllerも追加しましょう。

@RestController
@RequestMapping("/")
public class IndexController {

    static class CustomRepresentationModel extends RepresentationModel<CustomRepresentationModel> {
        public CustomRepresentationModel(Link initialLink) {
            super(initialLink);
        }
    }

    @GetMapping
    public CustomRepresentationModel index() {
        return new CustomRepresentationModel(linkTo(CRUDController.class).withRel("crud"));
    }
}

6. JUnitテスト

テストに戻ると、 MockMvc インスタンスを使用してサービスを呼び出し、要求と応答を文書化できます。

まず、すべてのMockMvc呼び出しが、それ以上の構成なしで自動的に文書化されるようにするために、alwaysDo()メソッドを使用できます。

this.mockMvc = MockMvcBuilders
  //...
  .alwaysDo(document("{method-name}", 
    preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
  .build();

この設定により、 MockMvc 呼び出しごとに、デフォルトのスニペットがテストメソッドの名前でフォルダーに作成されます。 また、prettyPrint()プリプロセッサを適用すると、スニペットがより読みやすい方法で表示されます。

いくつかの呼び出しのカスタマイズを続けましょう。

リンクを含むインデックスページを文書化するには、静的links()メソッドを使用できます。

@Test
public void indexExample() throws Exception {
    this.mockMvc.perform(get("/")).andExpect(status().isOk())
      .andDo(document("index", 
        links(linkWithRel("crud").description("The CRUD resource")), 
        responseFields(subsectionWithPath("_links")
          .description("Links to other resources"))
        responseHeaders(headerWithName("Content-Type")
          .description("The Content-Type of the payload"))));
}

ここでは、 linkWithRel() リンクを文書化する方法 /crud。

Content-Type ヘッダーを応答に追加するには、 headerWithName()メソッドを使用して文書化し、 responseHeaders()メソッドに追加します。

また、responseFields()メソッドを使用して応答ペイロードを文書化しています。 これは、subsectionWithPath()またはfieldWithPath()メソッドを使用して、応答のより複雑なサブセクションまたは単一のフィールドを文書化するために使用できます。

応答ペイロードと同様に、 requestPayload():を使用して要求ペイロードを文書化することもできます。

@Test
public void crudCreateExample() throws Exception {
    Map<String, Object> crud = new HashMap<>();
    crud.put("title", "Sample Model");
    crud.put("body", "http://www.baeldung.com/");
       
    this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON)
      .content(this.objectMapper.writeValueAsString(crud)))
      .andExpect(status().isCreated())
      .andDo(document("create-crud-example", 
        requestFields(fieldWithPath("id").description("The id of the input"),
          fieldWithPath("title").description("The title of the input"),
          fieldWithPath("body").description("The body of the input"),
        ))));
}

この例では、タイトルフィールドと本文フィールドを持つ CrudInput モデルを受信し、CREATEDステータスを送信するPOSTリクエストを文書化しました。 各フィールドはfieldWithPath()メソッドを使用して文書化されます。

リクエストとパスのパラメーターを文書化するには、requestParameters()メソッドとpathParameters()メソッドを使用できます。 どちらの方法も parameterWithName() 各パラメータを説明する方法:

@Test
public void crudDeleteExample() throws Exception {
    this.mockMvc.perform(delete("/crud/{id}", 10)).andExpect(status().isOk())
      .andDo(document("crud-delete-example", 
      pathParameters(
        parameterWithName("id").description("The id of the input to delete")
      )));
}

ここでは、idパスパラメーターを受け取る削除エンドポイントを文書化しました。

Spring REST Docsプロジェクトには、ドキュメントにあるフィールド制約やリクエストパーツなど、さらに強力なドキュメント機能が含まれています。

7. 出力

ビルドが正常に実行されると、RESTドキュメントスニペットの出力が生成され、 target/generated-snippetsフォルダーに保存されます。

生成された出力には、サービス、「curl」呼び出しなどのRESTサービスの呼び出し方法、RESTサービスからのHTTP要求と応答、およびサービスへのリンク/エンドポイントに関する情報が含まれます。

CURLコマンド

----
$ curl 'http://localhost:8080/' -i
----

HTTP –REST応答

[source,http,options="nowrap"]
----
HTTP/1.1 200 OK
Content-Type: application/hal+json;charset=UTF-8
Content-Length: 93

{
  "_links" : {
    "crud" : {
      "href" : "http://localhost:8080/crud"
    }
  }
}
----

8. スニペットを使用したドキュメントの作成

より大きなドキュメントでスニペットを使用するには、Asciidoc includeを使用してスニペットを参照できます。この場合、 src /docsapi-guideというドキュメントを作成しました。 adoc

そのドキュメントで、リンクスニペットを参照したい場合は、ドキュメントを処理するときにMavenに置き換えられるプレースホルダー {snippets} を使用して、リンクスニペットを含めることができます。

==== Links

include::{snippets}/index-example/links.adoc[]

9. AsciidocsMavenプラグイン

APIガイドをAsciidocから読み取り可能な形式に変換するために、ビルドライフサイクルにMavenプラグインを追加できます。 これを有効にするには、いくつかの手順があります。

  1. Asciidoctorプラグインをpom.xmlに適用します
  2. 依存関係のセクションで説明されているように、testCompile構成のspring-restdocs-mockmvcへの依存関係を追加します
  3. 生成されたスニペットの出力場所を定義するプロパティを構成します
  4. test タスクを構成して、スニペットディレクトリを出力として追加します
  5. asciidoctorタスクを構成します
  6. 生成されたスニペットをドキュメントに含めるときに使用できるスニペットという名前の属性を定義します
  7. タスクをtestタスクに依存させて、ドキュメントが作成される前にテストが実行されるようにします
  8. snippetsディレクトリを入力として構成します。 生成されたすべてのスニペットは、このディレクトリの下に作成されます

pom.xml のプロパティとしてスニペットディレクトリを追加して、Asciidoctorプラグインがこのパスを使用してこのフォルダーの下にスニペットを生成できるようにします。

<properties>
    <snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
</properties>

ビルドからAsciidocスニペットを生成するためのpom.xmlのMavenプラグイン構成は次のとおりです。

<plugin> 
    <groupId>org.asciidoctor</groupId>
    <artifactId>asciidoctor-maven-plugin</artifactId>
    <version>1.5.6</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>
                <sourceDirectory>src/docs/asciidocs</sourceDirectory>
                <outputDirectory>target/generated-docs</outputDirectory>
             </configuration>
	 </execution>
    </executions>
</plugin>

10. APIドキュメント生成プロセス

Mavenビルドが実行され、テストが実行されると、すべてのスニペットは、構成された target/generated-snippetsディレクトリーの下のsnippetsフォルダーに生成されます。 スニペットが生成されると、ビルドプロセスでHTML出力が生成されます。

生成されたHTMLファイルはフォーマットされて読み取り可能であるため、RESTドキュメントをすぐに使用できます。 Mavenビルドが実行されるたびに、ドキュメントも最新の更新で生成されます。

11. 結論

ドキュメントがない方が間違ったドキュメントよりも優れていますが、SpringRESTドキュメントはRESTfulサービスの正確なドキュメントを生成するのに役立ちます。

Springの公式プロジェクトとして、Spring MVC Test、 WebTestClient 、RESTAssuredの3つのテストライブラリを使用して目標を達成します。 ドキュメントを生成するこの方法は、RESTfulAPIを開発およびドキュメント化するためのテスト駆動アプローチをサポートするのに役立ちます。

この記事のコードに基づくサンプルプロジェクトは、リンクされたGitHubリポジトリにあります。