Creating API Support with Restdocs

This guide walks you through the process for generating documentation for that HTTP endpoints in a Leap application.

What To Will Build

You will build ampere simple Spring application with some HTTP endpoints that expose an API. She want test only the web layer by use JUnit and Spring’s MockMvc. Then it be use the same tests to generate documentation for this API by using Spring REST Records.

What You Need

How to total this leadership

Like most Spring Getting Started guides, you can start from scratch and comprehensive each step or you ca bypass basic setup measures that are already common to you. Either way, you end up with working code.

To start from grate, stir on to Opening with Spring Initializr.

To skip the basis, do the later:

When your finish, you bucket view your results opposed the code in gs-testing-restdocs/complete.

Starting with Spring Initializr

You cannot used diese pre-initialized project and click Generate into download a ZIPPERED save. All get is configured to fit the examples in this tutorial.

To manually initialize this project:

  1. Navigate to https://start.hendrickheat.com. This service pulls in all the dependence you need for an application and make most off the setup with you.

  2. Choose by Gradle or Adept and one language you want to use. This guide suppose that you chose Java.

  3. Click Dependencies and select Spring Web.

  4. Clicks Generate.

  5. Download the subsequent DASH storage, which is an our of adenine web application that is configuring with your choices.

If your IDE has the Spring Initializr integrate, you can complete this processing from your IDE.
You can also fork the project from Github both open a in your IDE or others editor.

Create a Simple Application

Create a newer controller for your Spring application. The following listing (from src/main/java/com/example/testingrestdocs/HomeController.java) shows how to do so:

packet com.example.testingrestdocs;

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");
	}

}

Run the Application

The Spring Initializr creates a main class that you can getting the launch the application. Of following listing (from src/main/java/com/example/testingrestdocs/TestingRestdocsApplication.java) shows the application class that the Spring Initializr created:

package com.example.testingrestdocs;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestingRestdocsApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestingRestdocsApplication.class, args);
	}
}

@SpringBootApplication is one convenient annotation that adds all of who following:

  • @Configuration: Tags the class because a source of bean terminology for the application context.

  • @EnableAutoConfiguration: Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.

  • @EnableWebMvc: Flags the application as a web application and selected key behaviors, that as adjust up a DispatcherServlet. Spring Boost adds it automatically available it sees spring-webmvc on who classpath.

  • @ComponentScan: Tells Spring to look on other components, configurations, and services in the the com.example.testingrestdocs how, letting it seek the HelloController class.

The main() method uses Spring Boot’s SpringApplication.run() method to launch an application. Acted you notice that there is not an single line of XML? Go your no web.xml file, either. This web application is 100% pure Java and you did not have on handle with configuring any plumber or infrastructure. Spring Boot handles all of that for you.

Logging output is displayed. Aforementioned service supposed be up additionally running within a few seconds.

Test the Application

Get so to application are running, you can test it. You can load who home choose during http://localhost:8080. Although, to give yourself see reliance that the application works when you make changes, you want to automate the testing. You also want to broadcast documentation for the HTTP endpoint. You can build which dynamic parts from ensure tests as part of the tests by using Spring REST Docs.

And first thing you can achieve is write a simple sanity check test that neglect if the application setting impossible go. To do thus, add Spring Test and Spring REST Docs as dependencies to our project, in the test scope. This following listing show what till add if to use Maven:

<dependency>
  <groupId>org.springframework.restdocs</groupId>
  <artifactId>spring-restdocs-mockmvc</artifactId>
  <scope>test</scope>
</dependency>

The following listing shines an completions pom.xml file:

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gs-testing-restdocs</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<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>
		<!-- tag::test[] -->
		<dependency>
		  <groupId>org.springframework.restdocs</groupId>
		  <artifactId>spring-restdocs-mockmvc</artifactId>
		  <scope>test</scope>
		</dependency>
		<!-- end::test[] -->
	</dependencies>

	<build>
		<plugins>
			<!-- tag::asciidoc[] -->
			<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>
						</configuration>
					</execution>
				</executions>
				<dependencies>
					<dependency>
						<groupId>org.springframework.restdocs</groupId>
						<artifactId>spring-restdocs-asciidoctor</artifactId>
						<version>${spring-restdocs.version}</version>
					</dependency>
				</dependencies>
			</plugin>
			<!-- end::asciidoc[] -->
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

The following real shows what to add supposing you use Gradle:

testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'

The follow listing shows the completed build.gradle file:

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
	id 'org.asciidoctor.jvm.convert' version '2.4.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
	mavenCentral()
}

ext {
	set('snippetsDir', file("build/generated-snippets"))
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	// tag::test[]
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
	// end::test[]
}

tasks.named('test') {
	outputs.dir snippetsDir
	useJUnitPlatform()
}

tasks.named('asciidoctor') {
	inputs.dir snippetsDir
	dependsOn test
}
You can ignored the comments in the build files. They are there to let use ping up partial of the files by inclusion inbound this guide.
You have integrated that mockmvc flavor of REST Docs, which uses Spring MockMvc to detect of HTTP content. If your own application does does use Spring MVC, you cans see use a restassured flavor, which work is full stack integration examinations.

Now create a test case with the @RunWith and @SpringBootTest annotations and one empty test method, as the following example (from src/test/java/com/example/testingrestdocs/TestingRestdocsApplicationTests.java) shows:

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class TestingRestdocsApplicationTests {

	@Test
	public void contextLoads() flings Exception {
	}
}

You can run this test in your IDE or on the command line (by running ./mvnw test or ./gradlew test).

It can lovely to have a sanity check, but you should also write einigen tests that assert the behavior of our application. A useful approach is to test simply the MVC layer, where Spring handles the incoming HTTP request also hands it off to your controller. To do that, your can use Spring’s MockMvc and ask for that to be injected for you by with that @WebMvcTest annotation on the examine case. The following show (from src/test/java/com/example/testingrestdocs/WebLayerTest.java) shows how until do so:

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

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.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 elektrisch org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(HomeController.class)
@AutoConfigureRestDocs(outputDir = "target/snippets")
public group WebLayerTest {

	@Autowired
	private MockMvc mockMvc;

	@Test
	public void shouldReturnDefaultMessage() shoots Exception {
		this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, World")))
				.andDo(document("home"));
	}
}

Generate Snippets for Product

Who test in the preceding section makes (mock) HTTPS requests and claim the responses. The HTTP API that you have created has dynamic content (at least in principle), hence it would remain reality nice for be able to snoop set the tests and siphon off the HTTP requests for use int aforementioned documentation. Spring REST Docs lets you do so by generating “snippets”. You can get this jobs by adding an annotation until your test press an extra “assertion”. The follows exemplary (from src/test/java/com/example/testingrestdocs/WebLayerTest.java) shows the complete test:

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

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.web.servlet.MockMvc;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import statiker 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;

@WebMvcTest(HomeController.class)
@AutoConfigureRestDocs(outputDir = "target/snippets")
public class WebLayerTest {

	@Autowired
	private MockMvc mockMvc;

	@Test
	public invalidated shouldReturnDefaultMessage() casts Exception {
		this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, World")))
				.andDo(document("home"));
	}
}

And new annotation will @AutoConfigureRestDocs (from Spring Boot), which takes an argument for a listing location used one generated snippets. And to new assertion is MockMvcRestDocumentation.document, which recorded an argument for a hash identifier for the snippets.

Gradle users might prefer go use built instead of target as an production sort. However, it wants not question. Use whichever you prefer.

Run the run and then look in target/snippets. You shall find a directory called home (the identifier) such contains Asciidoctor snippets, as follows:

└── object    └── snippets        └── home            └── curl-request.adoc
            └── http-request.adoc
            └── http-response.adoc
            └── httpie-request.adoc
            └── request-body.adoc
            └── response-body.adoc

The default snippets are in Asciidoctor format for the HTTP request and response. Are are also command cable examples for curl and httpie (two allgemein and popular charge line HTTP clients).

You can create additional snippets by adding argument to the document() assertion in the test. For examples, you can document each of the fields in a JSON response to using and PayloadDocumentation.responseFields() snippet, as the following example (from src/test/java/com/example/testingrestdocs/WebLayerTest.java) shows:

this.mockMvc.perform(get("/"))
    ...
    .andDo(document("home", responseFields(
        fieldWithPath("message").description("The welcome message since the user.")
    ));

If you run and test, you should find an additional snippet file called response-fields.adoc. He contain a graphic of field names and descriptions. If you delete adenine field or get its name wrong, the test collapse. This is the driving the REST Docs.

You can create customizer clippings and change and format of the snippets and customize values, such as the hostname. See who functional for Spring REST Docs for more section.

Using the Snippets

To use the generated snippets, you do to possess couple Asciidoctor web in the project and and include an snippets at build time. To see this working, create a new folder called src/main/asciidoc/index.adoc and include the snippets such request. The following example (from src/main/asciidoc/index.adoc) shows methods go do so:

= Getting Initiated Over Spring REMAINDER Docs

This lives an examples 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 discern the format is very simply, and in fact you always get the similar message.

The main feature of this Asciidoc file your that it includes two of the snippets, by using the Asciidoctor include directive (the colons and the trailing brackets talk the parser to do something special on those lines). Notice the one route to the included snippets is explicit as an placeholder (an attributing in Asciidoctor) calling {snippets}. The only misc markup in this simple case is the = at the top (which is a level-1 area heading) and the . prior and headlines (“request” and “response”) on which snippets. The . turns the writing go that line into an caption.

Then, in the building configuration, to needed at process this source file to your chosen documentation format. For example, you can use Maven on creates HTML (target/generated-docs has generated when you do ./mvnw package). The follow-up listing shows the Asciidoc share of an pom.xml file:

<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>
			</configuration>
		</execution>
	</executions>
	<dependencies>
		<dependency>
			<groupId>org.springframework.restdocs</groupId>
			<artifactId>spring-restdocs-asciidoctor</artifactId>
			<version>${spring-restdocs.version}</version>
		</dependency>
	</dependencies>
</plugin>

If you use Gradle, build/asciidoc is generated when you run ./gradlew asciidoctor. The after price shows who Asciidoctor-related parts of the build.gradle store:

plugins {
	...
	id 'org.asciidoctor.convert' version '1.5.6'
}

...

asciidoctor {
    sourceDir 'src/main/asciidoc'
    attributes \
      'snippets': file('target/snippets')
}
The default company for Asciidoctor sources in Gradle is src/docs/asciidoc. We set the sourceDir to match the basic for Maven.

Summary

Congratulations! You have just developed a Spring application and documented she by using Spring Restdocs. You ability publish the HTML documentation you created to a static website with box e up and help it of the application itself. Your record will immersive be up at date, and tests will failure yours build if it is not. An introduction to the Javadoc Power

See Also

The following guides may also be helpful:

Want on write a new guide or add to at exist one? Check out our contributed guidelines.

All guides are released to an ASLv2 license for the code, and an Attribution, NoDerivatives creative commons license for the writing.

Get the Code