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
-
進入目錄
gs-testing-restdocs/initial
-
直接跳到 建立一個簡單的應用程式。
完成後,您可以對照 gs-testing-restdocs/complete
中的程式碼檢查您的結果。
從 Spring Initializr 開始
您可以使用這個預初始化的專案,然後點選 Generate 下載 ZIP 檔案。這個專案已經配置好,適合本教程中的示例。
手動初始化專案
-
導航到 https://start.spring.io。這個服務會引入應用程式所需的所有依賴,併為您完成大部分設定。
-
選擇 Gradle 或 Maven 以及您想使用的語言。本指南假定您選擇了 Java。
-
點選 Dependencies 並選擇 Spring Web。
-
點選 Generate。
-
下載生成的 ZIP 檔案,它是一個根據您的選擇配置好的 Web 應用程式的壓縮包。
如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成此過程。 |
您也可以從 Github Fork 專案,然後在您的 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://:8080
載入主頁。然而,為了讓您在進行更改時對應用程式的工作更有信心,您希望自動化測試過程。您還希望釋出 HTTP 端點的文件。您可以使用 Spring REST Docs 作為測試的一部分生成這些測試的動態部分。
您首先可以做的是編寫一個簡單的健康檢查測試,如果應用程式上下文無法啟動,它就會失敗。為此,請將 Spring Test 和 Spring REST Docs 新增為專案的依賴項,作用域為 test。以下列表顯示瞭如果您使用 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
預設的程式碼片段是 Asciidoctor 格式的 HTTP 請求和響應。還有 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://: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}
。在這種簡單情況下,唯一的其他標記是頂部的 =
(它是一個一級章節標題)以及程式碼片段標題(“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 文件釋出到靜態網站,或者將其打包並從應用程式本身提供服務。您的文件將始終保持最新,如果文件沒有更新,測試將導致您的構建失敗。
另請參閱
以下指南可能也有幫助:
想編寫新指南或為現有指南做貢獻?請檢視我們的 貢獻指南。
所有指南的程式碼均採用 ASLv2 許可證釋出,文字內容則採用署名-禁止演繹(Attribution, NoDerivatives)知識共享許可協議釋出。 |