1.声明
当前内容主要用于测试和使用SpringBoot-data的RestDocs方式生成测试api
- spring-data-jpa方式操作内存h2数据库
- 使用restdocs生成测试文档
2.当前pom依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.18.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin> -->
<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>
<attributes>
<snippets>${project.build.directory}/generated-snippets</snippets>
</attributes>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
其中:<snippets>${project.build.directory}/generated-snippets</snippets>
,表示生成adoc文件的路径(就是测试后的分散文件)
3.基本的SpringBootDemo
1.配置类:Config .java
@Configuration
@EntityScan(basePackages = "com.hy.springboot.restdoc.entity")
@EnableJpaRepositories(basePackages = "com.hy.springboot.restdoc.dao")
public class Config {
}
2.实体类:User .java
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
3.dao层:UserRepository .java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.hy.springboot.restdoc.entity.User;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
4.controller层:UserController .java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.hy.springboot.restdoc.dao.UserRepository;
import com.hy.springboot.restdoc.entity.User;
@RestController
public class UserController {
@Autowired
UserRepository userRepository;
@RequestMapping(value = "/getUserById/{id}", produces = {
MediaType.APPLICATION_JSON_VALUE })
public User getUserById(@PathVariable Integer id) {
return userRepository.findById(id).get();
}
@RequestMapping(value = "/findAll")
public Object findAll(@PathVariable Integer id) {
return userRepository.findAll();
}
@RequestMapping(value = "/addUser", produces = {
MediaType.APPLICATION_JSON_VALUE })
public String addUser(User user) {
userRepository.save(user);
return "{\"result\":\"success\",\"msg\":\"添加用户成功!\"}";
}
}
注意这里的controller中的方法一定要返回json数据,否则在使用RestDocs的时候会出现JsonPath解析错误!
5.入口类:SpringBootDataRestDocApp .java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
/**
* @author admin
* @createTime 2021-03-01 11:53:24
* @description 当前内容主要用于测试和使用SpringBoot-data-restdoc
*
*/
@SpringBootApplication
public class SpringBootDataRestDocApp
{
public static void main( String[] args )
{
SpringApplication.run(SpringBootDataRestDocApp.class, args);
//String json="{\"id\":1,\"name\":\"admin\"}";
//DocumentContext documentContext = JsonPath.parse(json);
//Object read = JsonPath.read(json,"id");
//System.out.println(read);
}
}
4.测试类(即生成RestDocs的类)
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.results.ResultMatchers;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.request.PathParametersSnippet;
import org.springframework.restdocs.request.RequestDocumentation;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.hy.springboot.restdoc.config.Config;
import com.hy.springboot.restdoc.controller.UserController;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import javax.servlet.RequestDispatcher;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootDataRestDocApp.class)
@AutoConfigureRestDocs(outputDir = "target/generated-snippets")
@WebAppConfiguration
public class AppTest {
MockMvc mockMvc;
@Autowired
WebApplicationContext webApplicationContext;
@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
// 测试之前
@Before
public void startUp() {
// 通过MockMvcBuilders和当前web应用创建MockMvc
/*
* mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
* .apply(documentationConfiguration(this.restDocumentation)).build();
*/
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
.apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(document("{ClassName}/{methodName}")).build();
}
@Test
public void addUser() throws Exception {
// MockMvcResultMatchers.
// relaxedResponseFields
this.mockMvc
.perform(get("/addUser").param("name", "admin"))
.andDo(print()) // 打印请求信息
.andExpect(status().isOk()) // 期望是一个好请求
.andExpect(jsonPath("result", is(notNullValue())))
.andExpect(jsonPath("msg", is(notNullValue())))
.andDo(document("{ClassName}/{methodName}", responseFields(
fieldWithPath("result").description("当前操作的结果"),
fieldWithPath("msg").description("当前操作结果对应的消息")
)));
}
@Test
public void getUserById() throws Exception {
this.mockMvc.perform(get("/getUserById/{id}", 1))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("id", is(notNullValue())))
.andExpect(jsonPath("name", is(notNullValue())))
.andDo(document("{ClassName}/{methodName}", RequestDocumentation
.pathParameters(
RequestDocumentation.parameterWithName("id").description("需要查询的用户编号"))//
, responseFields(
fieldWithPath("id").description("添加后的自动生成用户编号"),
fieldWithPath("name").description("添加的用户名称"))));
}
}
注意事项:
document("{ClassName}/{methodName}")
表示生成文档样式:当前类名称/当前@Test方法名称,这个关系到后面的index.adoc中的表达式
jsonPath表示在返回的json数据中取出指定的key,如果返回不是json那么报错
RequestDocumentation.parameterWithName("id")表示路径{id}参数的名称
- fieldWithPath(“name”),表示字段描述
- 最重要的一个,
必须在当前项目的src下的main文件夹中创建asciidoc文件夹,最后在其中创建index.adoc
(index.adoc中主要为显示html的样式)
@AutoConfigureRestDocs(outputDir = "target/generated-snippets"),表示生成adoc的文件位置
5.编写index.adoc的内容
= API
:toc: left
:toclevels: 4
:toc-title: 接口目录
[[user]]
== 1.用户管理
[[user-getById]]
=== 根据用户id获取用户信息
operation::AppTest/getUserById[]
[[user-addUser]]
=== 添加用户信息
operation::AppTest/addUser[]
其中operation::AppTest/getUserById[]表示迭代AppTest文件夹中的getUserById文件夹中的所有adoc文件并进行转换写入
6.maven启动打包
使用cmd切换到该项目的pom文件下,执行mvn package
直到出现全部绿即可!
此时生成一个html文件位于:当前项目\target\generated-docs这里
使用浏览器打开即可:
此时操作成功!
7.总结
1.SpringBoot-data中使用RestDocs感觉比较复杂,需要手动编写测试环境,并且,对应测试的方法中每个返回的结果都必须是json数据
2.小心index.adoc中的表达式,错了一个就会出现无法连接其他的adoc文件的情况
3.当前的测试方法中必须填写andExpect即返回json的属性,否则报错,并且响应的状态也需要写清楚,个人感觉比Swagger复杂多了