Restdocs で API ドキュメント作成

このガイドでは、Spring アプリケーションで HTTP エンドポイントのドキュメントを生成するプロセスを順を追って説明します。

構築するもの

API を公開するいくつかの HTTP エンドポイントを使用して、簡単な Spring アプリケーションを構築します。JUnit と Spring の MockMvc を使用して、Web レイヤーのみをテストします。次に、同じテストを使用して、Spring REST Docs を使用して API のドキュメントを生成します。

必要なもの

本ガイドの完成までの流れ

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializr から開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-testing-restdocs/complete のコードに対して結果を確認できます。

Spring Initializr から開始

IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。

プロジェクトを手動で初期化するには:

  1. IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。

  2. Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。

  3. 依存関係をクリックして、Spring Web を選択します。

  4. 生成をクリックします。

  5. 結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。

EclipseIntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。
プロジェクトを Github からフォークして、IDE または他のエディターで開くこともできます。

シンプルなアプリケーションを作成する

Spring アプリケーション用の新しいコントローラーを作成します。次のリスト(src/main/java/com/example/testingrestdocs/HomeController.java から)は、その方法を示しています。

package com.example.testingrestdocs;

import java.util.Collections;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

	@GetMapping("/")
	public Map<String, Object> greeting() {
		return Collections.singletonMap("message", "Hello, World");
	}

}

アプリケーションの実行

Spring Initializr は、アプリケーションの起動に使用できる main クラスを作成します。次のリスト(src/main/java/com/example/testingrestdocs/TestingRestdocsApplication.java から)は、Spring Initializr が作成したアプリケーションクラスを示しています。

package com.example.testingrestdocs;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestingRestdocsApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestingRestdocsApplication.class, args);
	}
}

@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。

  • @Configuration: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。

  • @EnableAutoConfiguration: クラスパス設定、他の Bean、さまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。

  • @EnableWebMvc: アプリケーションを Web アプリケーションとしてフラグを立て、DispatcherServlet のセットアップなどの主要な動作をアクティブにします。Spring Boot は、クラスパスで spring-webmvc を検出すると自動的に追加します。

  • @ComponentScancom.example.testingrestdocs パッケージ内の他のコンポーネント、構成、サービスを探すように Spring に指示し、HelloController クラスを見つけさせます。

main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。XML の単一行がないことに気づきましたか? web.xml ファイルもありません。この Web アプリケーションは 100% 純粋な Java であり、接続機能やインフラストラクチャの構成に対処する必要はありませんでした。Spring Boot がすべてを処理します。

ロギング出力が表示されます。サービスは数秒以内に起動して実行されるはずです。

アプリケーションをテストする

アプリケーションが実行されたため、テストできます。http://localhost:8080 でホームページをロードできます。ただし、変更を加えたときにアプリケーションが機能するという自信を高めるために、テストを自動化する必要があります。また、HTTP エンドポイントのドキュメントを公開することもできます。Spring REST Docs を使用して、テストの一部としてそのテストの動的部分を生成できます。

最初にできることは、アプリケーションコンテキストを開始できない場合に失敗する単純なサニティチェックテストを作成することです。これを行うには、テストスコープでプロジェクトへの依存関係として Spring Test と Spring REST Docs を追加します。次のリストは、Maven を使用する場合に追加するものを示しています。

<dependency>
  <groupId>org.springframework.restdocs</groupId>
  <artifactId>spring-restdocs-mockmvc</artifactId>
  <scope>test</scope>
</dependency>

次のリストは、完成した pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gs-testing-restdocs</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- tag::test[] -->
		<dependency>
		  <groupId>org.springframework.restdocs</groupId>
		  <artifactId>spring-restdocs-mockmvc</artifactId>
		  <scope>test</scope>
		</dependency>
		<!-- end::test[] -->
	</dependencies>

	<build>
		<plugins>
			<!-- tag::asciidoc[] -->
			<plugin>
				<groupId>org.asciidoctor</groupId>
				<artifactId>asciidoctor-maven-plugin</artifactId>
				<version>1.5.8</version>
				<executions>
					<execution>
						<id>generate-docs</id>
						<phase>prepare-package</phase>
						<goals>
							<goal>process-asciidoc</goal>
						</goals>
						<configuration>
							<backend>html</backend>
							<doctype>book</doctype>
						</configuration>
					</execution>
				</executions>
				<dependencies>
					<dependency>
						<groupId>org.springframework.restdocs</groupId>
						<artifactId>spring-restdocs-asciidoctor</artifactId>
						<version>${spring-restdocs.version}</version>
					</dependency>
				</dependencies>
			</plugin>
			<!-- end::asciidoc[] -->
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

次の例は、Gradle を使用する場合に追加するものを示しています。

testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'

次のリストは、完成した build.gradle ファイルを示しています。

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
	id 'org.asciidoctor.jvm.convert' version '2.4.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
	mavenCentral()
}

ext {
	set('snippetsDir', file("build/generated-snippets"))
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	// tag::test[]
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
	// end::test[]
}

tasks.named('test') {
	outputs.dir snippetsDir
	useJUnitPlatform()
}

tasks.named('asciidoctor') {
	inputs.dir snippetsDir
	dependsOn test
}
ビルドファイル内のコメントは無視できます。このガイドに含めるファイルの一部を選択できるようにするためにあります。
Spring MockMvc を使用して HTTP コンテンツをキャプチャーする REST ドキュメントの mockmvc フレーバーを組み込みました。独自のアプリケーションが Spring MVC を使用しない場合は、フルスタック統合テストで機能する restassured フレーバーを使用することもできます。

次の例(src/test/java/com/example/testingrestdocs/TestingRestdocsApplicationTests.java から)が示すように、@RunWith および @SpringBootTest アノテーションと空のテストメソッドを使用してテストケースを作成します。

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class TestingRestdocsApplicationTests {

	@Test
	public void contextLoads() throws Exception {
	}
}

IDE またはコマンドラインで(./mvnw test または ./gradlew test を実行して)このテストを実行できます。

健全性チェックがあると便利ですが、アプリケーションの動作をアサートするテストを作成する必要もあります。便利なアプローチは、Spring が受信 HTTP リクエストを処理し、コントローラーに渡す MVC レイヤーのみをテストすることです。これを行うには、Spring の MockMvc を使用し、テストケースで @WebMvcTest アノテーションを使用して挿入するようにリクエストできます。次の例(src/test/java/com/example/testingrestdocs/WebLayerTest.java から)は、その方法を示しています。

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(HomeController.class)
@AutoConfigureRestDocs(outputDir = "target/snippets")
public class WebLayerTest {

	@Autowired
	private MockMvc mockMvc;

	@Test
	public void shouldReturnDefaultMessage() throws Exception {
		this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, World")))
				.andDo(document("home"));
	}
}

ドキュメントのスニペットを生成する

前のセクションのテストでは、HTTP リクエスト(モック)を作成し、レスポンスをアサートします。作成した HTTP API には(少なくとも原則として)動的なコンテンツが含まれているため、テストでスパイし、ドキュメントで使用する HTTP リクエストを吸い上げることができれば非常に便利です。Spring REST Docs では、「スニペット」を生成することでこれを実現できます。テストにアノテーションと追加の「アサーション」を追加することで、これを機能させることができます。次の例(src/test/java/com/example/testingrestdocs/WebLayerTest.java から)は、完全なテストを示しています。

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(HomeController.class)
@AutoConfigureRestDocs(outputDir = "target/snippets")
public class WebLayerTest {

	@Autowired
	private MockMvc mockMvc;

	@Test
	public void shouldReturnDefaultMessage() throws Exception {
		this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, World")))
				.andDo(document("home"));
	}
}

新しいアノテーションは @AutoConfigureRestDocs (Spring Boot から)です。これは、生成されたスニペットのディレクトリの場所の引数を取ります。そして、新しいアサーションは MockMvcRestDocumentation.document で、スニペットの文字列識別子の引数を取ります。

Gradle ユーザーは、target ではなく build を出力ディレクトリとして使用することを好む場合があります。しかし、問題ではありません。好きな方を使用してください。

テストを実行してから target/snippets を調べます。次のように、Asciidoctor (英語) スニペットを含む home (識別子)というディレクトリを見つける必要があります。

└── target
    └── snippets
        └── home
            └── curl-request.adoc
            └── http-request.adoc
            └── http-response.adoc
            └── httpie-request.adoc
            └── request-body.adoc
            └── response-body.adoc

HTTP リクエストとレスポンスのデフォルトのスニペットは Asciidoctor 形式です。curl および httpie (2 つの一般的で一般的なコマンドライン HTTP クライアント)のコマンドライン例もあります。

テストで document() アサーションに引数を追加することにより、追加のスニペットを作成できます。例: 次の例(src/test/java/com/example/testingrestdocs/WebLayerTest.java から)が示すように、PayloadDocumentation.responseFields() スニペットを使用して、JSON レスポンスの各フィールドをドキュメント化できます。

this.mockMvc.perform(get("/"))
    ...
    .andDo(document("home", responseFields(
        fieldWithPath("message").description("The welcome message for the user.")
    ));

テストを実行すると、response-fields.adoc という追加のスニペットファイルが見つかります。フィールド名と説明の表が含まれています。フィールドを省略するか、名前を間違えると、テストは失敗します。これが REST ドキュメントの力です。

カスタムスニペットを作成し、スニペットの形式を変更し、ホスト名などの値をカスタマイズできます。詳細については、Spring REST Docs (英語) のドキュメントを参照してください。

スニペットを使用する

生成されたスニペットを使用するには、プロジェクトに Asciidoctor コンテンツをいくつか用意し、ビルド時にスニペットを含めます。この動作を確認するには、src/main/asciidoc/index.adoc という新しいファイルを作成し、必要に応じてスニペットを含めます。次の例(src/main/asciidoc/index.adoc から)は、その方法を示しています。

= Getting Started With Spring REST Docs

This is an example output for a service running at http://localhost:8080:

.request
include::{snippets}/home/http-request.adoc[]

.response
include::{snippets}/home/http-response.adoc[]

As you can see the format is very simple, and in fact you always get the same message.

この Asciidoc ファイルの主な機能は、Asciidoctor include ディレクティブを使用して 2 つのスニペットを含むことです(コロンと末尾の括弧は、これらの行で特別なことを行うようにパーサーに指示します)。含まれるスニペットへのパスが {snippets} と呼ばれるプレースホルダー(Asciidoctor の attribute)として表されていることに注意してください。この単純な場合の他のマークアップは、最上部の = (レベル 1 セクションの見出し)と、スニペットのキャプション(「リクエスト」と「レスポンス」)の前の . です。. は、その行のテキストをキャプションに変換します。

次に、ビルド構成で、このソースファイルを選択したドキュメント形式に処理する必要があります。例: Maven を使用して HTML を生成できます(./mvnw package を実行すると target/generated-docs が生成されます)。次のリストは、pom.xml ファイルの Asciidoc 部分を示しています。

<plugin>
	<groupId>org.asciidoctor</groupId>
	<artifactId>asciidoctor-maven-plugin</artifactId>
	<version>1.5.8</version>
	<executions>
		<execution>
			<id>generate-docs</id>
			<phase>prepare-package</phase>
			<goals>
				<goal>process-asciidoc</goal>
			</goals>
			<configuration>
				<backend>html</backend>
				<doctype>book</doctype>
			</configuration>
		</execution>
	</executions>
	<dependencies>
		<dependency>
			<groupId>org.springframework.restdocs</groupId>
			<artifactId>spring-restdocs-asciidoctor</artifactId>
			<version>${spring-restdocs.version}</version>
		</dependency>
	</dependencies>
</plugin>

Gradle を使用する場合、./gradlew asciidoctor を実行すると build/asciidoc が生成されます。次のリストは、build.gradle ファイルの Asciidoctor 関連の部分を示しています。

plugins {
	...
	id 'org.asciidoctor.convert' version '1.5.6'
}

...

asciidoctor {
    sourceDir 'src/main/asciidoc'
    attributes \
      'snippets': file('target/snippets')
}
Gradle の Asciidoctor ソースのデフォルトの場所は src/docs/asciidoc です。Maven のデフォルトに一致するように sourceDir を設定します。

要約

おめでとう! Spring アプリケーションを開発し、Spring Restdocs を使用してドキュメント化しました。作成した HTML ドキュメントを静的な Web サイトに公開するか、パッケージ化してアプリケーション自体から提供できます。ドキュメントは常に最新の状態になり、ビルドが失敗するとテストは失敗します。

関連事項

次のガイドも役立ちます。

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語)

すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。

コードを入手する