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");
}
}
使用 Restdocs 建立 API 文件
本指南將引導您完成為 Spring 應用程式中的 HTTP 端點產生文件的過程。
您將建構的內容
您將建構一個簡單的 Spring 應用程式,其中包含一些公開 API 的 HTTP 端點。您將僅使用 JUnit 和 Spring 的 MockMvc
來測試 Web 層。然後,您將使用相同的測試,透過 Spring REST Docs 來產生 API 文件。
您需要的東西
-
約 15 分鐘
-
您慣用的文字編輯器或 IDE
-
Java 17 或更新版本
-
您也可以將程式碼直接匯入您的 IDE
如何完成本指南
如同大多數 Spring 入門指南,您可以從頭開始並完成每個步驟,或者您可以跳過您已熟悉的基礎設定步驟。無論哪種方式,您最終都會得到可運作的程式碼。
若要從頭開始,請繼續前往 從 Spring Initializr 開始。
若要跳過基礎知識,請執行以下操作
-
下載 並解壓縮本指南的原始碼儲存庫,或使用 Git 克隆它:
git clone https://github.com/spring-guides/gs-testing-restdocs.git
-
cd 進入
gs-testing-restdocs/initial
-
跳到 建立簡單的應用程式。
當您完成時,您可以將您的結果與 gs-testing-restdocs/complete
中的程式碼進行比較。
從 Spring Initializr 開始
您可以使用這個 預先初始化的專案,然後點擊「產生」以下載 ZIP 檔案。此專案已設定為符合本教學中的範例。
若要手動初始化專案
-
導覽至 https://start.spring.io。此服務會提取應用程式所需的所有相依性,並為您完成大部分的設定。
-
選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇 Java。
-
點擊相依性,然後選擇 Spring Web。
-
點擊產生。
-
下載產生的 ZIP 檔案,這是一個使用您的選擇設定的 Web 應用程式的封存檔。
如果您的 IDE 具有 Spring Initializr 整合,您可以從您的 IDE 完成此流程。 |
您也可以從 Github 分叉專案,並在您的 IDE 或其他編輯器中開啟它。 |
建立簡單的應用程式
為您的 Spring 應用程式建立一個新的控制器。以下清單(來自 src/main/java/com/example/testingrestdocs/HomeController.java
)顯示如何操作
執行應用程式
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
:告知 Spring Boot 開始根據類別路徑設定、其他 bean 和各種屬性設定來新增 bean。 -
@EnableWebMvc
:將應用程式標記為 Web 應用程式並啟動關鍵行為,例如設定DispatcherServlet
。當 Spring Boot 在類別路徑上看到spring-webmvc
時,它會自動新增它。 -
@ComponentScan
:告知 Spring 在com.example.testingrestdocs
套件中尋找其他組件、組態和服務,使其找到HelloController
類別。
main()
方法使用 Spring Boot 的 SpringApplication.run()
方法來啟動應用程式。您是否有注意到沒有任何一行 XML?也沒有 web.xml
檔案。這個 Web 應用程式是 100% 純 Java,而且您不必處理設定任何基礎結構或基礎設施。Spring Boot 為您處理所有這些。
會顯示記錄輸出。服務應在幾秒鐘內啟動並執行。
測試應用程式
現在應用程式正在執行,您可以測試它。您可以載入位於 https://127.0.0.1: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
}
您可以忽略建置檔案中的註解。它們在那裡是為了讓我們可以挑選檔案的部分內容以包含在本指南中。 |
您已包含 REST Docs 的 mockmvc 風格,它使用 Spring MockMvc 來擷取 HTTP 內容。如果您自己的應用程式未使用 Spring MVC,您也可以使用 restassured 風格,它適用於完整堆疊整合測試。 |
現在建立一個具有 @RunWith
和 @SpringBootTest
註解以及一個空白測試方法的測試案例,如下列範例(來自 src/test/java/com/example/testingrestdocs/TestingRestdocsApplicationTests.java
)所示
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
)。
進行健全性檢查很好,但您也應該編寫一些測試來斷言我們應用程式的行為。一個有用的方法是僅測試 MVC 層,其中 Spring 處理傳入的 HTTP 請求並將其傳遞給您的控制器。若要執行此操作,您可以使用 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 使用者可能更喜歡使用 build 而不是 target 作為輸出目錄。但是,這並不重要。使用您喜歡的任何一個。 |
執行測試,然後查看 target/snippets
。您應該會找到一個名為 home
(識別碼)的目錄,其中包含 Asciidoctor 程式碼片段,如下所示
└── 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
(兩個常見且受歡迎的命令列 HTTP 客戶端)的命令列範例。
您可以透過將引數新增至測試中的 document()
斷言來建立其他程式碼片段。例如,您可以使用 PayloadDocumentation.responseFields()
程式碼片段來記錄 JSON 回應中的每個欄位,如下列範例(來自 src/test/java/com/example/testingrestdocs/WebLayerTest.java
)所示
this.mockMvc.perform(get("/"))
...
.andDo(document("home", responseFields(
fieldWithPath("message").description("The welcome message for the user.")
));
如果您執行測試,您應該會找到一個名為 response-fields.adoc
的額外程式碼片段檔案。它包含一個欄位名稱和描述的表格。如果您省略欄位或名稱錯誤,測試將會失敗。這就是 REST Docs 的強大之處。
您可以建立自訂程式碼片段並變更程式碼片段的格式,以及自訂值,例如主機名稱。請參閱 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 https://127.0.0.1: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
指令(冒號和尾隨括號告訴剖析器在這些行上執行一些特殊操作)。請注意,包含的程式碼片段的路徑表示為一個預留位置(Asciidoctor 中的 attribute
),稱為 {snippets}
。在這個簡單的案例中,唯一的其他標記是頂部的 =
(它是第 1 級的章節標題)和程式碼片段上標題(「request」和「response」)之前的 .
。.
將該行上的文字變成標題。
然後,在建置組態中,您需要將此原始檔處理成您選擇的文件格式。例如,您可以使用 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 。我們將 sourceDir 設定為與 Maven 的預設值相符。 |
摘要
恭喜!您剛剛開發了一個 Spring 應用程式,並使用 Spring Restdocs 為其編寫文件。您可以將您建立的 HTML 文件發佈到靜態網站,或將其封裝並從應用程式本身提供。您的文件將始終保持最新,如果文件不是最新,測試將會使您的建置失敗。