使用Spring REST docs & JUnit 5創建API服務文檔

在前後端分離盛興的時代,REST API是前後端通信的必要途徑。一個好的REST API,不僅符合RESTful規範,還需要有一個高質量的API服務文檔。對於API服務文檔來講,難點不是創建文檔,而是如何更高效的維護文檔,讓文檔和代碼的變化保持同步。錯誤的API服務文檔,會讓使用者走入歧途,還不如沒有文檔的好。

Spring REST Docs

值得開心的是,在Spring全家桶中就用這樣的工具來滿足我們的要求。Spring REST Docs採用手寫文檔和自動生成片段相結合的方式來創建API文檔。Asciidoc是手寫文檔的格式,這種文檔格式在Spring全家桶中廣廣泛使用,Spring Reference document就是使用它生成的。

Spring REST Docs依據單元測試時所需的HTTP請求及其響應自動生成單元測試的文檔。

配置

我們都知道現在流行的Java項目管理工具有Maven和Gradle兩種。在Spring Framework的高級版本,將項目管理工具從Maven切換到了Gradle。因此,我們也採用Gradle來管理我們的項目。

build.gradle配置

buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.8.RELEASE'
}
}
plugins {
id "org.asciidoctor.convert" version "1.5.9.2"
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'eclipse'

apply plugin: 'io.spring.dependency-management'
repositories {
mavenLocal()
maven { url 'https://repo.spring.io/libs-snapshot' }
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
ext {
snippetsDir = file('build/generated-snippets')
}
ext['spring-restdocs.version'] = '2.0.5.BUILD-SNAPSHOT'
dependencies {
asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor'
compile 'org.springframework.boot:spring-boot-starter-web'
testCompile('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'junit', module: 'junit;'
}
testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc'
testCompile 'org.junit.jupiter:junit-jupiter-api'
testRuntime 'org.junit.jupiter:junit-jupiter-engine'
}
test {
outputs.dir snippetsDir
useJUnitPlatform()
}
asciidoctor {
inputs.dir snippetsDir
dependsOn test
}
bootJar {
dependsOn asciidoctor
from("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
eclipseJdt.onlyIf { false }
cleanEclipseJdt.onlyIf { false }

在這裡,我們的單元測試JUnit使用JUnit 5版本,它在Spring 5中得到了很好的支持。

像其他的Spring Boot項目一樣,我們需要創建一個Application。

package io.github.tinyking.spotlighs.restdocs;
import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* RestdocsApplication.
*
* @author tinyking
* @version 1.0
* @since 2019/10/15
*/
@SpringBootApplication
public class RestdocsApplication {
public static void main(String[] args) {
SpringApplication.run(RestdocsApplication.class, args);
}
}

創建一個簡單的Controller,用來做的HTTP API測試

package io.github.tinyking.spotlighs.restdocs;

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

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

/**

* SampleController.

*

* @author tinyking

* @version 1.0

* @since 2019/10/15

*/

@RestController

public class SampleController {

@GetMapping("/")

public String index() {

return "Hello, World";

}

}

接下來,我們需要為Application創建一個使用JUnit 5單元測試

package io.github.tinyking.spotlighs.restdocs;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* RestdocsApplicationTest.
*
* @author tinyking
* @version 1.0
* @since 2019/10/15
*/
@SpringBootTest
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
public class RestdocsApplicationTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@BeforeEach
public void setup(RestDocumentationContextProvider restDocumentationContextProvider) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.apply(documentationConfiguration(restDocumentationContextProvider)).build();
}
@Test
public void sample() throws Exception {
this.mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andDo(document("sample"));
}
}

手寫文檔

就像一開始說的,我們需要手寫文檔+自動生成來實現REST API服務文檔。上面所有的操作都是為了自動生成片段,下面我們來創建一個手寫文檔。

spotlight-spring-restdocs/src/docs/asciidoc/index.adoc

= 基於JUnit5的Spring restdocs實例

tinyking;

:doctype: book

:icons: font

:source-highlighter: highlightjs

Http curl請求示例:

include::{snippets}/sample/curl-request.adoc[]

Http請求:

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

Http響應:

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

這就是一個簡單的手寫+自動的文檔,裡面的curl-request.adoc、http-request.adoc、

http-response.adoc都是根據單元測試自動生成的。

生成API文檔

接下來我們通過命名來生成API文檔

gradle bootJar

執行完成後,可以找到build/asciidoc/html5/index.html ,直接在瀏覽器中打開,如下:


使用Spring REST docs & JUnit 5創建API服務文檔


總結

通過使用Spring REST docs,可以讓我們快速的生成API服務文檔,基於單元測試可以確保API文檔和代碼保持一致。並且Spring REST Docs的方式不像Swagger,它不會入侵我們的業務代碼,保證我們代碼的整潔性。


分享到:


相關文章: