Introdução ao Spring REST Docs
1. Visão geral
Spring REST Docs gera documentação para serviços RESTful que é precisa e legível. Ele combina documentação escrita à mão com trechos de documentos gerados automaticamente produzidos com os testes Spring.
2. Vantagens
Uma filosofia importante por trás do projeto é o uso de testes para produzir a documentação. Isso garante que a documentação sempre gerada corresponda com precisão ao comportamento real da API. Além disso, a saída está pronta para ser processada porAsciidoctor, uma cadeia de ferramentas de publicação centrada na sintaxe AsciiDoc. Esta é a mesma ferramenta usada para gerar a documentação do Spring Framework.
Essas abordagens reduzem as limitações impostas por outras estruturas. O Spring REST Docs produz documentação precisa, concisa e bem estruturada. Essa documentação permite que os consumidores de serviços da Web obtenham as informações necessárias com o mínimo de esforço.
A ferramenta tem outras vantagens, como:
-
snippets de solicitação de curl e http são gerados
-
documentação fácil de empacotar no arquivo jar do projeto
-
fácil adicionar informações extras aos snippets
-
suporta JSON e XML
Os testes que produzem os snippets podem ser escritos usando o suporte ao teste Spring MVC, Spring WebfluxWebTestClient ou REST-Assured.
Em nossos exemplos, vamos usar testes Spring MVC, mas usar os outros frameworks é muito semelhante.
3. Dependências
A maneira ideal de começar a usar o Spring REST Docs em um projeto é usando um sistema de gerenciamento de dependências. Aqui, estamos usando o Maven como ferramenta de construção, então a dependência abaixo pode ser copiada e colada em seu POM:
org.springframework.restdocs
spring-restdocs-mockmvc
2.0.0.RELEASE
Você também pode verificar o Maven Central para uma nova versão da dependênciahere.
Em nosso exemplo, precisamos da dependênciaspring-restdocs-mockmvc, pois estamos usando o suporte de teste Spring MVC para criar nossos testes.
Se quisermos escrever testes usando WebTestClient ou REST Assured, precisaremos das dependênciasspring-restdocs-webtestclientespring-restdocs-restassured.
4. Configuração
Conforme mencionado, usaremos a estrutura Spring MVC Test para fazer solicitações aos serviços REST que devem ser documentados. A execução do teste produz trechos de documentação para a solicitação e a resposta resultante.
Podemos usar a biblioteca com os testes JUnit 4 e JUnit 5. Vamos ver a configuração necessária para cada um.
4.1. Configuração JUnit 4
A primeira etapa na geração de snippets de documentação para testes JUnit 4 édeclare a public JUnitRestDocumentation field that is annotated as a JUnit @Rule.
A regraJUnitRestDocumentation é configurada com o diretório de saída no qual os snippets gerados devem ser salvos. Por exemplo, este diretório pode ser o diretório de criação do Maven:
@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
Em seguida, configuramos o contextoMockMvc para que seja configurado para produzir a documentação:
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
O objetoMockMvc é configurado usando um MockMvcRestDocumentationConfigurer. Uma instância dessa classe pode ser obtida do métododocumentationConfiguration() estático emorg.springframework.restdocs.mockmvc.MockMvcRestDocumentation.
4.2. Configuração JUnit 5
Para trabalhar com um teste JUnit 5, temos que estender o teste com a classeRestDocumentationExtension:
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
@SpringBootTest
public class ApiDocumentationJUnit5IntegrationTest { //... }
Essa classe é configurada automaticamente com um diretório de saída/target/generated-snippets ao usar o Maven ou/build/generate-snippets para o Gradle.
Em seguida, temos que configurar a instânciaMockMvc em um método@BeforeEach:
@BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation)).build();
}
Se não estivermos usando JUnit para os testes, temos que usar a classeManualRestDocumentation.
5. Serviço RESTful
Vamos criar um serviço CRUD RESTful que possamos documentar:
@RestController
@RequestMapping("/crud")
public class CRUDController {
@GetMapping
public List read(@RequestBody CrudInput crudInput) {
List returnList = new ArrayList();
returnList.add(crudInput);
return returnList;
}
@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public HttpHeaders save(@RequestBody CrudInput crudInput) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(
linkTo(CRUDController.class).slash(crudInput.getTitle()).toUri());
return httpHeaders;
}
@DeleteMapping("/{id}")
public void delete(@PathVariable("id") long id) {
// delete
}
}
Então, vamos também adicionar umIndexController que retorna uma página com um link para o endpoint de baseCRUDController:
@RestController
public class IndexController {
@GetMapping("/")
public ResourceSupport index() {
ResourceSupport index = new ResourceSupport();
index.add(linkTo(CRUDController.class).withRel("crud"));
return index;
}
}
6. Testes JUnit
De volta aos testes, podemos usar a instânciaMockMvc para chamar nossos serviços e documentar a solicitação e a resposta.
Primeiro,to make sure every MockMvc call is automatically documented without any further configuration we can use the alwaysDo() method:
this.mockMvc = MockMvcBuilders
//...
.alwaysDo(document("{method-name}",
preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
.build();
Essa configuração garante que a cada chamada deMockMvc, os snippets padrão sejam criados em uma pasta com o nome do método de teste. Além disso, a aplicação do pré-processadorprettyPrint() exibe os fragmentos de uma maneira mais legível.
Vamos continuar personalizando algumas de nossas ligações.
Para documentar nossa página de índice que contém um link, podemos usar o métodolinks() estático:
@Test
public void indexExample() throws Exception {
this.mockMvc.perform(get("/")).andExpect(status().isOk())
.andDo(document("index",
links(linkWithRel("crud").description("The CRUD resource")),
responseFields(subsectionWithPath("_links")
.description("Links to other resources"))
responseHeaders(headerWithName("Content-Type")
.description("The Content-Type of the payload"))));
}
Aqui, estamos usando o métodolinkWithRel() para documentar um link para/crud.
Para adicionar um cabeçalhoContent-Type à resposta, estamos documentando-o usando o métodoheaderWithName() e adicionando-o ao métodoresponseHeaders().
We’re also documenting the response payload using the responseFields() method. Isso pode ser usado para documentar uma subseção mais complexa da resposta ou um único campo usando os métodos subsectionWithPath () ou fieldWithPath ().
Semelhante à carga útil de resposta,we can also document the request payload using requestPayload():
@Test
public void crudCreateExample() throws Exception {
Map crud = new HashMap<>();
crud.put("title", "Sample Model");
crud.put("body", "http://www.example.com/");
this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON)
.content(this.objectMapper.writeValueAsString(crud)))
.andExpect(status().isCreated())
.andDo(document("create-crud-example",
requestFields(fieldWithPath("id").description("The id of the input"),
fieldWithPath("title").description("The title of the input"),
fieldWithPath("body").description("The body of the input"),
))));
}
Neste exemplo, documentamos nossa solicitação POST que recebe um modeloCrudInput com os campos de título e corpo e envia um status CREATED. Each field is documented using the fieldWithPath() method.
To document request and path parameter, we can use the requestParameters() and pathParameters() methods. Ambos os métodos usam um métodoparameterWithName() para descrever cada parâmetro:
@Test
public void crudDeleteExample() throws Exception {
this.mockMvc.perform(delete("/crud/{id}", 10)).andExpect(status().isOk())
.andDo(document("crud-delete-example",
pathParameters(
parameterWithName("id").description("The id of the input to delete")
)));
}
Aqui, documentamos nosso endpoint de exclusão que recebe um parâmetro de caminhoid.
O projeto Spring REST Docs contém funcionalidades de documentação ainda mais poderosas, como restrições de campo e partes de solicitação que podem ser encontradas emdocumentation.
7. Resultado
Assim que a compilação for executada com sucesso, a saída dos snippets de documentos REST será gerada e salva na pastatarget/generated-snippets:
A saída gerada terá as informações sobre o serviço, como chamar o serviço REST, como chamadas "curl", solicitação e resposta HTTP do serviço REST e links / pontos de extremidade para o serviço:
Comando CURL
$ curl 'http://localhost:8080/' -i
HTTP - Resposta REST
[source,http,options="nowrap"]
HTTP/1.1 200 OK Content-Type: application/hal+json;charset=UTF-8 Content-Length: 93
{"_links": {"crud": {"href": "http: // localhost: 8080 / crud"}}}
8. Usando Snippets para Criar Documentação
Para usar os snippets em um documento maior, você pode referenciá-los usando Asciidocincludes.. Em nosso caso, criamos um documento emsrc/docs chamadoapi-guide.adoc:
Nesse documento, se desejarmos fazer referência ao snippet de links, podemos incluí-lo, usando um marcador{snippets} que será substituído pelo Maven quando processar o documento:
==== Links
Unresolved directive in spring-rest-docs.adoc - include::{snippets}/index-example/links.adoc[]
9. Plugins Asciidocs Maven
Para converter o guia da API do Asciidoc em um formato legível, podemos adicionar um plug-in Maven ao ciclo de vida da construção. Existem várias etapas para habilitar isso:
-
Aplique o plugin Asciidoctor aopom.xml
-
Adicione uma dependência emspring-restdocs-mockmvc na configuraçãotestCompile conforme mencionado na seção de dependências
-
Configure uma propriedade para definir o local de saída para os trechos gerados
-
Configure a tarefatest para adicionar o diretório de snippets como uma saída
-
Configure a tarefaasciidoctor
-
Defina um atributo chamadosnippets que pode ser usado ao incluir os snippets gerados em sua documentação
-
Faça a tarefa depender da tarefatest para que os testes sejam executados antes de a documentação ser criada
-
Configure o diretóriosnippets como entrada. Todos os snippets gerados serão criados nesse diretório
Adicione o diretório de snippet como uma propriedade empom.xml para que o plug-in Asciidoctor possa usar este caminho para gerar os snippets nesta pasta:
${project.build.directory}/generated-snippets
A configuração do plugin Maven empom.xml para gerar os snippets Asciidoc da compilação é a seguinte:
org.asciidoctor
asciidoctor-maven-plugin
1.5.6
generate-docs
package
process-asciidoc
html
book
${snippetsDirectory}
src/docs/asciidocs
target/generated-docs
10. Processo de geração de documentos API
Quando a compilação do Maven é executada e os testes são executados, todos os snippets serão gerados na pasta de snippets no diretóriotarget/generated-snippets configurado. Depois que os trechos são gerados, o processo de criação gera saída HTML.
O arquivo HTML gerado é formatado e legível, portanto a documentação REST está pronta para uso. Sempre que a compilação do Maven é executada, os documentos também são gerados com as atualizações mais recentes.
11. Conclusão
Não ter documentação é melhor que documentação incorreta, mas os documentos do Spring REST ajudarão a gerar documentação precisa para os serviços RESTful.
Como um projeto oficial do Spring, ele atinge seus objetivos usando três bibliotecas de teste: Spring MVC Test,WebTestCliente REST Assured. Esse método de geração de documentação pode ajudar a oferecer suporte a uma abordagem orientada a testes para desenvolver e documentar APIs RESTful.
Você pode encontrar um projeto de exemplo com base no código deste artigo emlinked GitHub repository.