Spring REST Docs example

Greeting service api guide

This year in May I attended the Spring I/O conference where I heard about Spring REST docs. Two things captured my attention:

  • Documentation is generated from unit tests
  • Tests fail if documentation is not done properly.

After the conference I checked out Spring REST docs reference and I created a sample REST service to see it in practice. The code is very simple and it is the classic Hello World.

Spring REST docs uses Asciidoctor to generate the documentation so configuring maven is the first step.

Relevant information from this configuration is:

  • phase – If it is set to prepare-package documentation is available to be packaged. Spring boot can serve it as static content when application is running.
  • sourceDirectory – This is where Asciidoctor documentation templates can be found. I’ll get back to them later.
  • snippets – This attribute specifies the place where tests will generate documentation snippets. This attribute is used in Asciidoctor templates to include generated snippets in the documentation.
  • outputDirectory – It specifies where final documentation is generated.

The next step is to create the test class:

Documentation is done in two steps by this test class:

  • First step is setting up Spring MVC test.
    • restDocumentation junit rule is configured with the output directory into which generated snippets should be written.
    • mockMvc needs to know about this configuration and this is done by the static documentationConfiguration() method on org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.
  • The second step is to document how the API should be used. This is done in each test method by static document() method on the same org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.

The first test method greetingGetWithProvidedContent tests what happens if the greeting service is called with name parameter set:

  • The first parameter specifies the folder name where snippets for this method will be generated. In this case it is parameterized by the class and method names with {class-name}/{method-name}. The result is greeting-controller-test/greeting-get-with-provided-content.
  • requestParameters documents request parameters and generates request-parameters.adoc snippet.
  • responseFields documents response fields and generates response-fields.adoc snippet. All response fields need to appear in generated response, otherwise the test fails. optionalContent field is not returned in our result and I marked it as optional to demonstrate this possibility. It can happen in practice for a field to be returned only in certain situations.

The second test method greetingGetWithDefaultContent() tests what happens if the greeting service is called without name parameter. In this case it makes sense to document only what is different:

  • responseFields documents only content field because its value changes in this situation. To avoid documenting id parameter again I marked it as ignored.

Documentation snippets are generated now by test classes, we need a way to merge it all together. This is where our Asciidoctor skills are needed. We need to create a file in src/docs/asciidocs folder (see sourceDirectory from asciidoctor-maven-plugin configuration):

The most important aspect is how to include generated snippets. Each test method generates by default three snippets:

  • curl-request.adoc – Contains a sample curl command to access tested resource
  • http-request.adoc – Contains the request used by the test method
  • http-response.adoc – Contains the response generated by the test method

Besides these three default snippets our test methods generate two additional snippets:

  • request-parameters.adoc – Contains a table with documented request parameters
  • response-fields.adoc – Contains a table with documented response fields

After running mvn clean package generated documentation can be found in target/generated-docs folder:

generated documentation

We generated the documentation, so let’s check now what happens when our documenting code is out of sync with the implementation.

First scenario is to change request parameter from name to content. I updated the controller and how test accesses the resource but I missed to update the documenting code.

The test fails with:

The second scenario is to miss to document a field. In greetingGetWithDefaultContent() I will not document id field:

Test fails with:

The last scenario is to document a field that is not returned. I will remove optional() from documenting optionalContent field on greetingGetWithProvidedContent() method:

Test fails with:

I didn’t use Spring REST docs to document real production code but I’m planning to give it a try.