Pamiętacie wpis o najlepszych praktykach REST’owych? Jedną z wyszczególnionych tam praktyk było dokumentowanie naszego API. Wymieniłem tam także kilka najpopularniejszych rozwiązań. Jedno z nich, czyli Swagger opisałem już na blogu w artykule #10 Spring Boot – Swagger2 – dokumentujemy API. Dziś czas na kolejne podejście do dokumentacji, tym razem od strony testów. Test Driven Documentation jest techniką, która polega na tworzeniu dokumentacji w oparciu o testy.
Jak napisałem we wstępie, Spring Rest Docs wykorzystuje podejście, w którym pisanie dokumentacji odbywa się poprzez wykorzystanie testów. W testach tworzone są snippety, które następnie łączone są w jeden plik korzystając ze składni ASCIIDoc’a. Cała dokumentacja frameworku Spring napisana jest przy jej użyciu. ASCIIDoc przetwarza tekst na HTML. Spring Rest Docs wspiera:
- Spring MVC Test – wykorzystany w przykładzie
- REST Assured
1. Maven
Standardowo zaczynamy od dodania zależności do Maven’a:
<dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-mockmvc</artifactId> <scope>test</scope> </dependency>
Oraz musimy skonfigurować proces budowania dokumentacji:
<build> <plugins> <plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>1.5.3</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>1.2.1.RELEASE</version> </dependency> </dependencies> </plugin> </plugins> </build>
2. Domena
Na szybko stwórzmy sobie encję Todo, która będzie dostępna pod endpointem /todos
.
@Data @AllArgsConstructor public class Todo { @Id @GeneratedValue private Long id; private String title; private String description; }
Teraz dodajmy controller:
@RestController @RequestMapping("/todos") class TodoController { @GetMapping @ResponseStatus(value = HttpStatus.OK) Todo getTodoByTitle(@RequestParam(value = "title", required = true) String title){ return new Todo(1l, "TDD", "Description"); } }
3. Dokumentacja
Możemy zacząć pisanie testów! Jeśli zdecydowaliśmy się korzystać ze Spring MVC Test musimy zdefiniować @Rule
dla klasy JUnitRestDocumentation
, która w konstruktorze przyjmuje folder, gdzie znajdą się wygenerowane snippety (snippety to elementy generowane przez Spring Rest Docs, które umieszczamy potem w naszej dokumentacji):
@Rule public JUnitRestDocumentation documentation = new JUnitRestDocumentation("target/generated-snippets");
Musimy teraz poinstruować nasz test, aby generował dokumentację:
@RunWith(SpringRunner.class) @SpringBootTest public class TodoDocumentation { @Rule public JUnitRestDocumentation documentation = new JUnitRestDocumentation("target/generated-snippets"); @Autowired private WebApplicationContext context; @Autowired private TodoRepository todoRepository; private MockMvc mockMvc; @Before public void setUp() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.documentation)) .build(); } @Test public void shouldReturnTodo() throws Exception { todoRepository.save(new Todo("TDD", "CodeCouple")); this.mockMvc.perform( get("/todos") .param("title", "TDD") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(document("todos", responseFields( fieldWithPath("id").description("Todo ID"), fieldWithPath("title").description("Todo title"), fieldWithPath("description").description("Todo description")), requestParameters( parameterWithName("title").description("Todo title")))); } }
Ważna część dla nas zaczyna się od statycznej metody document
. Tam umieszczamy testy odpowiedzialne za generowanie dokumentacji. Metoda responseFields
sprawdza, czy w odpowiedzi otrzymaliśmy pola: id, title, description. Jeśli tak, to zostaną one udokumentowane wraz z opisem zawartym w description
. Kolejna metoda requestParameters
służy do testu i opisu parametrów zawartych w naszym URI. Oczywiście istnieje dużo więcej metod.
Aby stworzyć plik HTML z dokumentacją należy dodać wzorcowy plik ASIIDOC’a, w moim przypadku dodałem plik manual.adoc z treścią poniżej. Domyślną ścieżką na ten plik jest src/main/asciidoc
. Można tę ścieżkę zmienić w pluginie, należy w sekcji build
dodać wpis <sourceDirectory>your/path/here</sourceDirectory>
= Todos API Reference CodeCouple.pl - Version 1.0.1; :doctype: book :icons: font :source-highlighter: highlightjs :toc: left :toclevels: 4 :sectlinks: [[headers]] == Headers Curl-request in CodeCouple.pl: include::{snippets}/todos/curl-request.adoc[] include::{snippets}/todos/http-request.adoc[] include::{snippets}/todos/http-response.adoc[] include::{snippets}/todos/response-fields.adoc[] include::{snippets}/todos/request-parameters.adoc[]
Teraz w pierwszej kolejności uruchamiamy test, który wygeneruje nam do folderu target/generated-snippets
snippety. Następnie odpalamy install
w Mavenie. W folderze target/generated-docs
powinien dodać nam się plik manual.html (ma taką samą nazwę jak nasz plik z ASCIIDoc’a). Ścieżkę, gdzie generować ma się dokumentacja możemy ustawić w sekcji build
, należy dodać wpis <outputDirectory>your/path/here</outputDirectory>
3.1 Automatyczne budowanie
Jeśli chcemy mieć zautomatyzowany proces budowania wystarczy dodać poniższy plugin. Od teraz dla wszystkich klas z Documetation w nazwie generowana będzie dokumentacja przy budowaniu Mavenowego artefaktu.
<
plugin
>
<
groupId
>org.apache.maven.plugins</
groupId
>
<
artifactId
>maven-surefire-plugin</
artifactId
>
<
configuration
>
<
includes
>
<
include
>**/*Documentation.java</
include
>
</
includes
>
</
configuration
>
</
plugin
>
4. Wynik
Wynikiem działania jest bardzo dobrze wyglądający HTML:
5. Github
Całość jak zawsze na GitHubie.