The aim of this extension to Spring REST Docs is to help you to write even less — both code and documentation. You still get the same nice documentation as with Spring REST Docs itself. The main benefit is that writing less and moving the documentation closer to the code increases the maintainability of the documentation.
In Spring REST Docs you have to add the documentation for your JSON with a DSL in your test. We moved this documentation to the POJO that represents your JSON object. You just add Javadoc to the fields and it will end up in the documentation.
Features:
- Jackson visitor that gathers the whole JSON structure and includes Javadoc and constraint annotations on the fields. It works for both request and response bodies. In addition to the constraint documentation support that is already in Spring REST Docs, the constraint message is automatically included in the documentation. Constraint groups are also supported.
- Path and query parameters can be documented automatically.
- A helper to document authentication.
- A snippet that includes all other snippets and thus helps you write even less.
FAQ
- Does it work with Spring Web MVC tests?
Yes, take a look at the example.
- Does it work with Spring WebFlux?
Yes, take a look at the example.
- Is Kotlin supported?
Yes, Spring Auto REST Docs 1.0.13 and 2.0.0 introduced Kotlin support. Instead of the Javadoc Doclet one has to use the Dokka extension spring-auto-restdocs-dokka-json. The extension is only available as 2.0.x, but works with both version of Spring Auto REST Docs.
- Which Java versions are supported?
The baseline is Java 8. Our focus is on the LTS versions 8 and 11, but we also try to support the latest Java release. One has to use a different Javadoc Doclet starting at Java 9. See Getting Started.
- Does it work with REST Assured tests?
Not at the moment. If you want this, your PR is welcome.
- Is Jackson required for automatic field documentation?
Yes, this project only includes a Jackson visitor so far.
- Is a multi-module project setup supported?
The JSON doclet and the REST Docs snippets produce files. Therefore, all files should be placed on the filesystem. JSON files will be additionally looked up from classpath, so modularization of codebase is fully supported. For advanced customization, multiple source directories for the JSON files can be configured (see usage).
- Can Spring REST Docs and Spring Auto REST Docs features be combined?
Yes, this even works in the same test. One can generate Spring REST Docs snippets and Spring Auto REST Docs snippets out of the same test. This is demonstrated in the example project.
- Can I regenerate the final HTML without running all tests?
Yes, if you do not need the snippets generated out of the test to be updated, Asciidoctor can be executed standalone. With Maven you can run mvn asciidoctor:process-asciidoc (where process-asciidoc is the goal specified in your Maven POM and might differ). With Gradle it can be done via ./gradlew asciidoctor if asciidoctor is the name of the Gradle task in your setup. You might have to remove dependsOn test depending on your setup to run it standalone.
- Is HATEOAS supported?
Yes, there is basic support for links and embedded resources.
- What should I do if I get a NoSuchBeanDefinitionException: No bean named 'webHandler' available exception?
This means that the servlet stack instead if the reactive one is active. Make sure that @EnableWebFlux is used.
Getting started
Requirements
Spring Auto REST Docs has the following minimum requirements:
- Java 8 or Kotlin 1.3
- Spring REST Docs 2.0.5.RELEASE (see documentation)
- Jackson has to be used for creating and parsing JSON
For Java 7 and Spring REST Docs 1.2.x support, please use the 1.0.x version of Spring Auto REST Docs.
For Java 9+ support, use spring-auto-restdocs-json-doclet-jdk9 as doclet dependency. Our support is focused on the latest Kotlin version, Java 8 and 11. In addition, we try to support the latest Java version that is supported by Spring.
Usage
- Setup project for Spring REST Docs
- Additional configuration for this extension:
Maven
<dependency>
<groupId>capital.scalable</groupId>
<artifactId>spring-auto-restdocs-core</artifactId>
<version>2.0.11</version>
<scope>test</scope>
</dependency>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<systemPropertyVariables>
<org.springframework.restdocs.outputDir>
${project.build.directory}/generated-snippets
</org.springframework.restdocs.outputDir>
<org.springframework.restdocs.javadocJsonDir>
${project.build.directory}/generated-javadoc-json
</org.springframework.restdocs.javadocJsonDir>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>generate-javadoc-json</id>
<phase>compile</phase>
<goals>
<goal>javadoc-no-fork</goal>
</goals>
<configuration>
<doclet>capital.scalable.restdocs.jsondoclet.ExtractDocumentationAsJsonDoclet</doclet>
<docletArtifact>
<groupId>capital.scalable</groupId>
<artifactId>spring-auto-restdocs-json-doclet</artifactId>
<version>2.0.11</version>
</docletArtifact>
<destDir>generated-javadoc-json</destDir>
<reportOutputDirectory>${project.build.directory}</reportOutputDirectory>
<useStandardDocletOptions>false</useStandardDocletOptions>
<show>package</show>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
- (Optional) Determines directory where snippets are saved. Defaults to generated-snippets in build directory.
- (Optional) Determines where JSON files are saved. Defaults to generated-javadoc-json in build directory. Multiple directories can be listed by separating them with ,. The directories are processed in order and only the first found JSON file is used.
- For Java 9/10/11 support, use spring-auto-restdocs-json-doclet-jdk9 as doclet dependency.
Gradle
configurations {
jsondoclet
}
ext {
javadocJsonDir = file("$buildDir/generated-javadoc-json")
}
dependencies {
testCompile group: 'capital.scalable', name: 'spring-auto-restdocs-core', version: '2.0.11'
jsondoclet group: 'capital.scalable', name: 'spring-auto-restdocs-json-doclet', version: '2.0.11'
}
task jsonDoclet(type: Javadoc, dependsOn: compileJava) {
source = sourceSets.main.allJava
classpath = sourceSets.main.compileClasspath
destinationDir = javadocJsonDir
options.docletpath = configurations.jsondoclet.files.asType(List)
options.doclet = 'capital.scalable.restdocs.jsondoclet.ExtractDocumentationAsJsonDoclet'
options.memberLevel = JavadocMemberLevel.PACKAGE
}
test {
systemProperty 'org.springframework.restdocs.outputDir', snippetsDir
systemProperty 'org.springframework.restdocs.javadocJsonDir', javadocJsonDir
dependsOn jsonDoclet
}
jar {
dependsOn asciidoctor
}
- (Optional) Determines directory where snippets are saved. Defaults to generated-snippets in build directory.
- (Optional) Determines where JSON files are saved. Defaults to generated-javadoc-json in build directory. Multiple directories can be listed by separating them with ,. The directories are processed in order and only the first found JSON file is used.
- For Java 9/10/11 support, use spring-auto-restdocs-json-doclet-jdk9 as doclet dependency.
- Configure MockMvc or WebTestClient
MockMvc Test class
@Autowired
private WebApplicationContext context;
@Autowired
protected ObjectMapper objectMapper;
protected MockMvc mockMvc;
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.alwaysDo(JacksonResultHandlers.prepareJackson(objectMapper))
.alwaysDo(MockMvcRestDocumentation.document("{class-name}/{method-name}",
Preprocessors.preprocessRequest(),
Preprocessors.preprocessResponse(
ResponseModifyingPreprocessors.replaceBinaryContent(),
ResponseModifyingPreprocessors.limitJsonArrayLength(objectMapper),
Preprocessors.prettyPrint())))
.apply(MockMvcRestDocumentation.documentationConfiguration(restDocumentation)
.uris()
.withScheme("http")
.withHost("localhost")
.withPort(8080)
.and().snippets()
.withDefaults(CliDocumentation.curlRequest(),
HttpDocumentation.httpRequest(),
HttpDocumentation.httpResponse(),
AutoDocumentation.requestFields(),
AutoDocumentation.responseFields(),
AutoDocumentation.pathParameters(),
AutoDocumentation.requestParameters(),
AutoDocumentation.description(),
AutoDocumentation.methodAndPath(),
AutoDocumentation.section()))
.build();
}
WebTestClient Test class
@Autowired
private ApplicationContext context;
protected WebTestClient webTestClient;
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@Before
public void setUp() throws Exception {
this.webTestClient = WebTestClient
.bindToApplicationContext(context)
.apply(springSecurity())
.configureClient()
.baseUrl("http://localhost:8080/")
.filter(documentationConfiguration(restDocumentation)
.snippets()
.withDefaults(WebTestClientInitializer.prepareSnippets(context),
CliDocumentation.curlRequest(),
HttpDocumentation.httpRequest(),
HttpDocumentation.httpResponse(),
AutoDocumentation.requestFields(),
AutoDocumentation.responseFields(),
AutoDocumentation.pathParameters(),
AutoDocumentation.requestParameters(),
AutoDocumentation.description(),
AutoDocumentation.methodAndPath(),
AutoDocumentation.section()))
.build();
}
Snapshot build
If you want to experiment with snapshot builds, add this repository:
Maven
<repositories>
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
Gradle
repositories {
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
}
Snippets
Method and path snippet
Method and path snippet takes a value (path) and method from RequestMapping annotation on the Controller’s method. The shorthand annotations GetMapping, PostMapping, etc. are supported as well.
Code
@GetMapping(value = "/items")
public ItemResponse getItems() { ... }
Result
GET /items
Description snippet
Description snippet outputs the Controller’s method’s Javadoc. Currently only basic br, p, ul and li tags are supported.
Code
/**
* Returns list of all items.
* <p>
* Use query parameters to filter the list by:
* <ul>
* <li>orderNumber</li>
* <li>type</li>
* </ul>
*/
@GetMapping...
public ItemResponse getItems() { ... }
Result
Returns list of all items.
Use query parameters to filter the list by:
- orderNumber
- type
Authorization snippet
Authorization snippet does not help in adding authorization to your tests, but it makes it easy to document your authorization information. Usually, a custom RequestPostProcessor is used to add authorization and this is also the place where you add the documentation.
Code
protected RequestPostProcessor userToken() {
return new RequestPostProcessor() {
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
// <Code to get access token>
request.addHeader("Authorization", "Bearer " + accessToken);
return documentAuthorization(request, "User access token required.");
}
};
}
@Test
public void addItem() throws Exception {
mockMvc.perform(post("/items").with(userToken())
...
}
Result
User access token required.
Path parameters snippet
Path parameters snippet automatically lists parameters in the path including their type, whether they are optional, and their Javadoc with constraints.
Code
/**
* @param id ID of the item.
*/
@GetMapping("{id}")
public ItemResponse getItem(@PathVariable String id) { ... }
Result
Configuration
Configuration available during MockMvc setup:
- failOnUndocumentedParams - if build should fail on at least one undocumented parameter
Request parameters snippet
Request parameters snippet automatically lists query parameters including their type, whether they are optional, and their Javadoc with constraints. If a default value is set, it will also be included.
Code
/**
* @param descMatch Lookup on description field.
* @param lang Lookup on language.
*/
@GetMapping("search")
public Page<ItemResponse> searchItem(
@RequestParam("desc") String descMatch,
@RequestParam(value= "language", required = false, defaultValue = "en") String lang
) { ... }
Result
Configuration
Configuration available during MockMvc setup:
- failOnUndocumentedParams - if build should fail on at least one undocumented parameter
Model Attribute snippet
Model Attribute snippet automatically recognises POJOs annotated with @ModelAttribute annotation. Depending on HTTP method, it generates either section for request parameters (GET) or request fields (non GET). This snippet works also for model attributes without annotation, as specified in "any other argument" section of Spring MVC reference documentation.
Code
@GetMapping("search")
public SearchResult search(@ModelAttribute SearchCriteria criteria){ ... }
class SearchCriteria {
/** Lookup on description field. */
@NotBlank
String descMatch;
/** Lookup on language. */
String lang;
}
Configuration
Configuration available during MockMvc setup:
- failOnUndocumentedFields - if build should fail on at least one undocumented field
Request headers snippet
Request headers snippet automatically lists headers with their type, whether they are optional, and their Javadoc with constraints. If a default value is set, it will also be included.
Code
/**
* @param lang Language we want the response in.
*/
@GetMapping("/all")
public ItemResponse getItem(
@RequestHeader(value = "Accept-Language", required = false, defaultValue = "en") String lang
) { ... }
Result
Request fields snippet
Request fields snippet automatically lists all fields of the request class, determined from @RequestBody parameter of Controller’s method. List includes name, type, whether the field is optional, and its Javadoc with constraints.
Code
@PostMapping
public void addItem(@RequestBody ItemUpdateRequest request) { ... }
static class ItemUpdateRequest {
/**
* Item ID.
*/
@NotBlank
private Integer id;
/**
* Item description.
*/
@Size(max = 20)
private String description;
}
Result
Configuration
Configuration available during MockMvc setup:
- failOnUndocumentedFields - if build should fail on at least one undocumented field
- requestBodyAsType - specified class should be considered as request type instead of endpoint method’s parameter
Response fields snippet
Response fields snippet automatically lists all fields of the response class, determined from return value of Controller’s method. List includes name, type, whether the field is optional, and its Javadoc with constraints.
Code
@GetMapping("{id}")
public ItemResponse getItem(@PathVariable String id) { ... }
static class ItemResponse {
/**
* Unique item ID.
*/
@NotBlank
private String id;
/**
* Item's order number.
*/
@Min(1)
private Integer orderNumber;
}
Result
Configuration
Configuration available during MockMvc setup:
- failOnUndocumentedFields - if build should fail on at least one undocumented field
- responseBodyAsType - specified class should be considered as response type instead of endpoint method’s return type
The section snippet combines most common snippets into one convenient file. It helps you being even more lazy, because a single line of AsciiDoc is sufficient to document one endpoint. Assuming of course that you already documented your code with Javadoc.
Asciidoc
include::{snippets}/your-endpoint/auto-section.adoc[]
Template itself
[[resources-{{link}}]]
=== {{title}}
include::auto-method-path.adoc[]
include::auto-description.adoc[]
{{#sections}}
==== {{header}}
{{#filenames}}
include::{{.}}.adoc[]
{{/filenames}}
{{/sections}}
Template placeholders