本指南指导您完成在Spring应用程序中为HTTP端点生成文档的过程。您将构建一个简单的Spring应用程序,其中一些HTTP端点公开了一个API。您将只使用JUnit和Spring的MockMVC测试Web层,然后使用相同的测试,用Spring REST Docs生成API文档。
目录结构如下
└── src
└── main
└── java
└── hello
└── app
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>
<groupId>org.springframework</groupId>
<artifactId>gs-testing-restdocs</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<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>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot将会你做如下的事:
- 将 classpath 里面所有用到的jar包构建成一个可执行的 JAR 文件,方便执行你的程序
- 搜索public static void main()方法并且将它当作可执行类
- 根据springboot版本,去查找相应的依赖类版本,当然你可以定义其它版本。
创建一个简单的应用程序
创建一个控制器
src/main/java/hello/HomeController.java
package hello;
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");
}
}
创建Application
src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
运行并测试程序
以Spring boot app方式运行Application,然后在浏览器打开 http://localhost:8080 。
但是为了让您自己更确信应用程序在您进行更改时能够正常工作,您需要自动化测试。您还希望公开HTTP端点的文档,并且可以使用 Spring REST Docs生成该端点的动态部分,作为测试的一部分。
您可以做的第一件事是编写一个简单的健全性测试,如果应用程序上下文无法启动,该测试将失败。首先,在测试范围中将 Spring Test 和Spring REST Docs作为依赖项添加到项目中。如果您使用Maven:
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
如果使用Gradle:
build.gradle
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile("org.springframework.restdocs:spring-restdocs-mockmvc")
然后使用@RunWith和@SpringBootTest注释以及空测试方法创建一个测试用例:
src/test/java/hello/ApplicationTest.java
package hello;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTest {
@Test
public void contextLoads() throws Exception {
}
}
您可以在您的IDE或命令行(mvn test或gradle test)上运行此测试,结果应该通过。
像这样进行健全性检查是很好的,但是您还应该编写一些测试来断言应用程序的行为。一种有用的方法是只测试MVC层,Spring处理传入的HTTP请求并将其传递给控制器。要做到这一点,您可以使用Spring的MockMvc,并通过在测试用例上使用@WebMvcTest注释注入:
src/test/java/hello/WebLayerTest.java
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class WebLayerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}
}
为文档生成代码段
上面的测试发出(模拟)HTTP请求并断言响应结果。您创建的HTTP API具有动态内容(至少在原则上是这样),因此能够监视测试和了解HTTP请求以便在文档中使用。 Spring REST Docs 允许您通过生成“片段”来实现这一点。只需在测试中添加一个注释和一个额外的“断言”,您就可以很容易地实现这一点。以下是完整的测试:
src/test/java/hello/WebLayerTest.java
package hello;
import org.junit.Test;
import org.junit.runner.RunWith;
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.context.junit4.SpringRunner;
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;
@RunWith(SpringRunner.class)
@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
└── httpie-request.adoc
└── curl-request.adoc
└── http-request.adoc
└── http-response.adoc
默认代码段采用Asciidoctor 格式,用于HTTP请求和响应,另外两行是为curl和httpie(两个流行的命令行HTTP客户端)的命令行请求示例。
您可以通过向测试中的 document() 断言添加参数来创建其他代码段。例如,您可以使用PayloadDocumentation.responseFields()片段在JSON响应中记录每个字段:
src/test/java/hello/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 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.
它的主要功能是使用Asciidoctor include指令(冒号和尾括号告诉解析器在这些行上做一些特殊的事情)包含2个片段。请注意,所包含代码段的路径表示为占位符-Asciidoctor中的“属性”-称为代码段。在这个简单的例子中,唯一的其他标记是顶部的“=”,这是一个级别1的节标题,以及代码段标题(“request”和“response”)前面的“.”。
然后在构建配置中,您需要将这个源文件处理成您选择的文档格式。例如,使用maven生成HTML(执行mvnw包时生成target/generated-docs):
pom.xml
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<sourceDocumentName>index.adoc</sourceDocumentName>
<backend>html</backend>
<attributes>
<snippets>${project.build.directory}/snippets</snippets>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
或者,如果使用Gradle(进行gradlew asciidoctor时生成build/asciidoc)
build.gradle
buildscript {
repositories {
...
jcenter()
}
...
dependencies {
...
classpath("org.asciidoctor:asciidoctor-gradle-plugin:1.5.3")
}
}
...
apply plugin: 'org.asciidoctor.convert'
asciidoctor {
sourceDir 'src/main/asciidoc'
attributes \
'snippets': file('target/snippets')
}
Asciidoctor Gradle插件不在Maven Central中,因此还必须将jcenter() 添加到Gradle中的buildscipt依赖项中。
Gradle中的asciidoctor源的默认位置是src/doc/asciidoc。我们只需要设置sourceDir,因为我们更改了位置以匹配maven的默认值。
祝贺你!您刚刚开发了一个Spring应用程序,并使用Spring Rest Docs对其进行了记录。您可以将创建的HTML文档发布到静态网站,或者打包并从应用程序本身提供服务。您的文档将始终是最新的,如果不是,测试将使构建失败。