From spring
Generate Spring REST API documentation from MockMvc or WebTestClient tests and publish snippets via Asciidoctor. Use this skill when generating REST API documentation from Spring tests with MockMvc or WebTestClient snippets, and publishing those snippets through Asciidoctor.
npx claudepluginhub ririnto/sinon --plugin springThis skill uses the workspace's default tool permissions.
Use this skill when generating REST API documentation from Spring tests with MockMvc or WebTestClient snippets, and publishing those snippets through Asciidoctor.
references/cookies.mdreferences/custom-snippets.mdreferences/ignored-fields.mdreferences/links.mdreferences/multipart.mdreferences/path-parameters.mdreferences/preprocessors.mdreferences/query-parameters.mdreferences/relaxed-payloads.mdreferences/subsection-payloads.mdreferences/webtestclient-surface.mdMandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Use this skill when generating REST API documentation from Spring tests with MockMvc or WebTestClient snippets, and publishing those snippets through Asciidoctor.
The latest released Spring REST Docs line is 4.0.0. On this line the ordinary supported documentation surfaces are MockMvc and WebTestClient, so older REST Assured guidance must stay out of the common path.
Use spring-rest-docs for test-driven request and response documentation, generated snippets, preprocessors, field and parameter descriptions, and publishing generated docs.
The ordinary Spring REST Docs job is:
| Surface | Start here when | Open a reference when |
|---|---|---|
| MockMvc | the project already tests servlet endpoints with MockMvc | stay in SKILL.md |
| WebTestClient | the endpoint is reactive or the project already uses WebTestClient | open references/webtestclient-surface.md |
| Payload descriptors | field descriptors are the real blocker | open the payload-specific references below |
| Hypermedia links | links are part of the contract | open references/links.md |
| Multipart requests | multipart parts are part of the contract | open references/multipart.md |
Use only the test module that matches the chosen test surface.
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
| Need | Artifact |
|---|---|
| Servlet tests with MockMvc | spring-restdocs-mockmvc |
| Reactive tests with WebTestClient | spring-restdocs-webtestclient |
| Asciidoctor integration | spring-restdocs-asciidoctor |
Switch the artifact to spring-restdocs-webtestclient when the project does not use MockMvc.
@WebMvcTest(OrderController.class)
@AutoConfigureRestDocs
class OrderDocumentationTests {
}
@AutoConfigureRestDocs wires the REST Docs infrastructure into the Boot test slice. When the output directory or URI rewriting must be customized, keep that customization explicit in the test or in spring.restdocs.* configuration.
spring:
restdocs:
uri:
scheme: https
host: api.example.com
port: 443
Keep published URI rewriting stable across local runs and CI jobs.
./mvnw package
./gradlew test asciidoctor
build/generated-snippets/
target/generated-snippets/
:snippets: build/generated-snippets
:snippets: target/generated-snippets
Keep the snippets directory stable so documentation includes do not drift across modules or CI jobs.
configurations {
create("asciidoctorExt")
}
dependencies {
"asciidoctorExt"("org.springframework.restdocs:spring-restdocs-asciidoctor")
}
val snippetsDir = file("build/generated-snippets")
tasks.test {
outputs.dir(snippetsDir)
}
tasks.asciidoctor {
configurations("asciidoctorExt")
inputs.dir(snippetsDir)
dependsOn(tasks.test)
}
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
</execution>
</executions>
<configuration>
<attributes>
<snippets>${project.build.directory}/generated-snippets</snippets>
</attributes>
</configuration>
</plugin>
orders-create or orders-get.@WebMvcTest(OrderController.class)
@AutoConfigureRestDocs
class OrderDocumentationTests {
@Autowired
MockMvc mvc;
@Test
void createOrder() throws Exception {
mvc.perform(post("/orders")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"sku\":\"ABC\",\"quantity\":2}"))
.andExpect(status().isCreated())
.andDo(document("orders-create", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields(fieldWithPath("sku").description("Stock keeping unit"), fieldWithPath("quantity").description("Quantity requested")), responseFields(fieldWithPath("id").description("Created order id"), fieldWithPath("status").description("Order status"))));
}
}
.andDo(document("orders-list", queryParameters(parameterWithName("status").description("Order status filter")), requestHeaders(headerWithName("X-Correlation-Id").description("Request correlation id")), responseFields(fieldWithPath("[].id").description("Order id"), fieldWithPath("[].status").description("Order status"))));
.andDo(document("orders-get", pathParameters(parameterWithName("orderId").description("Order identifier")), responseFields(fieldWithPath("id").description("Order id"), fieldWithPath("status").description("Order status"))));
.andDo(document("orders-get", preprocessRequest(modifyUris().scheme("https").host("api.example.com").removePort()), preprocessResponse(prettyPrint())));
= Orders API
:snippets: build/generated-snippets
== Create order
operation::orders-create[snippets='http-request,http-response,request-fields,response-fields']
orders-create
build/generated-snippets/orders-create/
operation::orders-create[snippets='http-request,http-response,response-fields']
Return: