#11. Rest Docs & JUnit 테스트 기초 설정
[목표]
API 명세서의 생성을 Postman 과 같은 Client Tool을 사용하여 수동으로 작성하는 것이 아닌,
Spring Rest Docs [테스트 코드 기반의 API 문서화 방식] - JUnit Test를 통해 자동 생성을 하고자 한다.
[Spring RestDocs]
정확하고 읽기 쉬운 RESTful 서비스에 대한 문서를 생성하는 라이브러리이며,
실제 테스트를 통해 문서의 snippets 을 생성하여 문서가 코드와 함께 항상 최신 상태가 되도록 보장한다.
API 문서는 MockMvc, document() 메서드를 사용해야 생성된다.
[순서]
1. src/docs/asciidoc 디렉토리 생성.
2. index.adoc 파일 생성.
3. src/main/resources/static/docs 디렉토리 생성
4. index.html 파일 생성.
5. build.gradle 추가.
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.11'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id "org.asciidoctor.jvm.convert" version "3.3.2" (1)
}
// (2)
ext {
set('snippetsDir', file("build/generated-snippets"))
}
// (3)
configurations {
asciidoctorExtensions
}
dependencies {
// (4)
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
// (5)
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
implementation 'com.google.code.gson:gson'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation 'org.mockito:mockito-core:5.2.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
}
// (6)
tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}
// (7)
tasks.named('asciidoctor') {
configurations "asciidoctorExtensions"
inputs.dir snippetsDir
dependsOn test
}
// (8)
task copyDocument(type: Copy) {
dependsOn asciidoctor // (8-1)
from file("${asciidoctor.outputDir}") // (8-2)
into file("src/main/resources/static/docs") // (8-3)
}
build {
dependsOn copyDocument // (9)
}
// (10)
bootJar {
dependsOn copyDocument // (10-1)
from ("${asciidoctor.outputDir}") { // (10-2)
into 'static/docs' // (10-3)
}
}
(1) .adoc 파일 확장자를 가지는 AsciiDoc 문서를 생성해주는 Asciidoctor 사용하기 위한 플러그인 추가
(2) ext 변수의 set() 메서드를 이용해 API 문서 스니펫이 생성될 경로 지정
(3) AsciiDoctor에서 사용되는 의존 그룹 지정하고 있다.
:asciidoctor task가 실행되면 내부적으로 (3)에서 지정한 asciidoctorExtensions라는 그룹을 지정.
(4) 'org.springframework.restdocs:spring-restdocs-mockmvc' 추가함으로써,
spring-restdocs-core와 spring-restdocs-mockmvc 의존 라이브러리가 추가.
(5) spring-restdocs-asciidoctor 의존 라이브러리를 추가. (3)에서 지정한 그룹에 의존 라이브러리가 포함.
(6) :test task 실행 시, API 문서 생성 스니펫 디렉토리 경로를 설정
(7) asciidoctor task 실행 시,
Asciidoctor 기능을 사용하기 위해 :asciidoctor task에 asciidoctorExtensions을 설정.
(8) :build task 실행 전에 실행되는 task. :copyDocument task가 수행되면
index.html 파일이 src/main/resources/static/docs에 copy되며,
index.html 파일은 API문서를 파일형태로 외부에 제공하기 위한 용도로 사용할 수 있다.
(8-1) :asciidoctor task가 실행된 후 task가 실행되도록 의존성을 설정.
(8-2) : build/docs/asciidoc/ 경로에 생성되는 index.html을 copy 후
(8-3) : src/main/resources/static/docs 경로로 index.html을 추가해준다.
(9) :build task가 실행되기 전에 :copyDocument task가 먼저 수행되도록 한다.
(10) 애플리케이션 실행 파일이 생성하는 :bootJar task 설정.
(10-1) :bootJar task 실행 전에 :copyDocument task가 실행되도록 의존성을 설정
(10-2), (10-3) Asciidoctor 실행으로 생성되는 index.html 파일을 jar 파일 안에 추가.
jar 파일에 index.html 을 추가해줌으로써, 웹 브라우저에서 접속(http://localhost:8080/docs/html) 후,
API 문서를 확인할 수 있다.
<중요>
(8)에서 copy되는 index.html은 외부에 제공하기 위한 용도
(10) index.html을 애플리케이션 실행 팡리인 jar 파일에 포함해서 웹 브라우저에서 API 문서 확인하는 용도
6. ApiDocumentUtils 인터페이스 생성.
public interface ApiDocumentUtils {
static OperationRequestPreprocessor getRequestPreProcessor() {
return preprocessRequest(prettyPrint());
}
static OperationResponsePreprocessor getResponsePreProcessor() {
return preprocessResponse(prettyPrint());
}
}
ApiDocumentUtils 인터페이스는 Spring RestDocs용 유틸리티 인터페이스 이다.
OperationRequestPreprocessor, OperationResponsePreprocessor은
Spring RestDocs에서 제공하는 인터페이스이며,
이를 통해 요청 및 응답이 문서화되기 전에 적용되어야하는 일반적인 사전 처리 작업을 정의할 수 있다.
getRequestPreProcessor(), getResponsePreProcessor() 메서드는 prettyPrint() 전처리를 사용하여
이는 JSON 이쁘게 출력하여 문서화된 요청과 응답의 가독성을 높이도록 한다.
실제로 test 코드를 작성할 때 그 결과 값을 이쁘게 받기 위해 아래와 같이 사용한다.
this.mockMvc.perform(get("/example"))
.andExpect(status().isOk())
.andDo(document("index",
ApiDocumentUtils.getRequestPreProcessor(),
ApiDocumentUtils.getResponsePreProcessor()
));
7. Test 클래스를 아래와 같이 생성한다.
8. JUnit Test 할땐 DB를 H2로 변경한다.
spring:
# JUnit Test 전용 H2
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:test
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
format_sql: true
9. test 디렉토리의 Application 실행 파일을 실행하면, 통과된 test 코드의 문서 스니핏이 생성된다.
해당 파일 실행 후 pass되는 메서드에 한해서,
test 코드 내부의 document()에 적은 식별자 이름대로 폴더가 생성되고 내부에 스니핏이 생성된다.
10. 초기에 생성해둔 아래 사진 경로의 index.adoc 파일은 API 문서 스니펫을 포함하는 템플릿 문서.
해당 파일을 실행시키고 아래와 같이 작성하자.
= 커피 주문 애플리케이션 // (1)
:sectnums: // A
:toc: left // B
:toclevels: 4 // C
:toc-title: Table of Contents // D
:source-highlighter: prettify // E
Hwang Jung Sik <jungsik.hwang@codestates.com> // (2)
v1.0.0, 2022.04.08 // (3)
// (4)
***
== MemberController
=== 회원 등록
.curl-request // 섹션의 제목
include::{snippets}/post-member/curl-request.adoc[]
// {snippets} : 해당 스니펫이 생성되는 디폴트 경로를 의미,
gradle에 설정한대로 snippetsDir 변수를 참조할 수 있다.
.http-request
include::{snippets}/post-member/http-request.adoc[]
.request-fields
include::{snippets}/post-member/request-fields.adoc[]
.http-response
include::{snippets}/post-member/http-response.adoc[]
.response-headers
include::{snippets}/post-member/response-headers.adoc[]
=== 회원 정보 수정
.curl-request
include::{snippets}/patch-member/curl-request.adoc[]
.http-request
include::{snippets}/patch-member/http-request.adoc[]
.path-parameters
include::{snippets}/patch-member/path-parameters.adoc[]
.request-fields
include::{snippets}/patch-member/request-fields.adoc[]
.http-response
include::{snippets}/patch-member/http-response.adoc[]
.response-fields
include::{snippets}/patch-member/response-fields.adoc[]
(1) : API 문서 제목 // =를 추가하면 되며 = 개수가 늘어날수록 글자는 작아진다.
(2) : API 문서 생성자의 정보
(3) : API 문서 생성 날짜
(4) : 테스트 케이스를 통해 생성한 API 문서 스니펫을 사용하는 부분.
<사용 양식>
include::{snippets}/스니펫 문서가 위치한 디렉토리/스니펫 문서파일명.adoc[]
<Asciidoc 문법>
A : 각 섹션에 넘버링해주는 명령어 :sectnums:
B :toc:는 목차를 문서의 어느 위치에 구성할지 설정. 왼쪽으로 가도록 left 설정함
C :toclevels: 목차에 표시할 제목의 level을 지정. 4로 지정했으므로 ==== 까지의 제목만 목차에 표시
D :toc-title: 목차의 제목을 지정
E : source-highligher: 문서에 표시되는 소스 코드 하이라이터 지정. prettify 지정함.
위의 설정대로 하면 아래와 같이 표시됨
F : ***은 단락을 구분 지을 수 있는 수평선을 추가.
G : 문단의 제목 다음에 한 라인 띄우고 한칸 들여쓰기하면 박스 문단을 사용할 수 있다.
H : CAUTION: 을 사용해서 경고 문구를 추가할 수 있다. NOTE: ,TIP: , IMPORTANT: ,WARINING :
[결과]
11. index.adoc 작성 이후 Gradle 탭 -> :boorJar or :build 명령 클릭 후 해당 명령이 완료되면,
src/main/resources/static/docs 디렉토리에 index.adoc 파일을 이용해 변환된 index.html 파일이 생성됨.