Spring Rest docs Asciidoctor(눈물의 똥꼬쇼)
대표님이 레스트독스로 api설명을 만들어서 보내달라 하셔서 테스트코드부터 쭉 짰다.
가장 먼저 build.gradle에 설정부터 넣어줬다.
plugins {
id 'org.asciidoctor.jvm.convert' version '3.3.2'
}
configurations {
asciidoctorExt
compileOnly {
extendsFrom annotationProcessor
}
}
ext {
set('snippetsDir', file("build/generated-snippets"))
}
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
asciidoctor {
dependsOn test
configurations 'asciidoctorExt'
sourceDir = file('src/docs/asciidoc') // 소스 디렉토리 지정
outputDir = file("build/docs/asciidoc") // 출력 디렉토리 지정
inputs.dir snippetsDir // 스니펫 디렉토리
}
test{
useJUnitPlatform()
outputs.dir snippetsDir
}
tasks.named('asciidoctor') {
inputs.dir snippetsDir
dependsOn test
}
tasks.register('copyApiDocument', Copy) { // 자동으로 생성된 문서를 옮겨주는 copyApiDocument task 선언
dependsOn asciidoctor // copyApiDocument를 수행하기전 asciidoctor를 수행하도록 선언
doFirst { // 작업이 실행되기전 수행하는 작업 선언
delete file("src/main/resources/static/docs") // 기존 문서 삭제
}
// 생성된 문서를 static/docs로 이동
from asciidoctor.outputDir
into file("src/main/resources/static/docs")
}
build {
dependsOn copyApiDocument // 프로젝트 빌드 전 copyApiDocument를 실행하도록 변경
}
Rest docs를 만드는 과정을 제대로 이해하고있지 않던 나는 요게 뭔소리래..하면서 시작했다.
그리고 일단 테스트케이스를 작성했다.
@WebMvcTest(CategoryController.class)
@MockBean(JpaMetamodelMappingContext.class)
@ExtendWith(RestDocumentationExtension.class)
@AutoConfigureRestDocs
public class CategoryControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private CategoryService categoryService;
@BeforeEach
void setup(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(MockMvcRestDocumentation.documentationConfiguration(restDocumentation))
.alwaysDo(document("{class-name}/{method-name}"))
.build();
}
@Test
void createCategory() throws Exception {
CategoryDto.Create createDto = new CategoryDto.Create();
createDto.setName("과목");
given(categoryService.createCategory(any(CategoryDto.Create.class))).willReturn(1L);
String requestBody = "{ \"name\": \"과목\" }";
mockMvc.perform(post("/api/boards/v1/category")
.header("X-Auth-UserId", "admin")
...
.content(requestBody))
.andExpect(status().isCreated())
.andExpect(content().string("1"))
.andDo(document("category/create",
requestFields(
fieldWithPath("name").description("The name of the new category")
),
responseBody()
)
);
}
...
}
테스트 케이스 작성에만 오전을 다쓴거같다.
겨우겨우 테스트케이스를 통과시키고
./gradlew clean asciidoctor 명령어로 빌드시키려는데 자꾸 x 100000 빌드가 실패했다.
could not find or load main class worker.org.gradle.process.internal.worker.gradleworkermain
이런 에러가 계속나고 계속해서 연구했다.
./gradlew asciidoctor --info 명령어로 로그를 보니,
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE
Skipping task ':testClasses' as it has no actions.
Resolve mutations for :test (Thread[Execution worker Thread 7,5,main]) started.
:test (Thread[Execution worker Thread 7,5,main]) started.
Could not write standard input to Gradle Test Executor 8.
java.io.IOException: ?뚯씠?꾧? ?ロ엳??以묒엯?덈떎
<==Could not write standard input to Gradle Test Executor 9.
<==Could not write standard input to Gradle Test Executor 10.
<==Could not write standard input to Gradle Test Executor 11.
<==Could not write standard input to Gradle Test Executor 12.
<==Could not write standard input to Gradle Test Executor 13.
<==Could not write standard input to Gradle Test Executor 14.
> Task :test FAILED
Excluding []
Caching disabled for task ':test' because:
Build cache is disabled
Task ':test' is not up-to-date because:
Task has failed previously.
이런식으로로그가 나왔다. 진짜 엄청나게 블로그를 찾아본결과 나같은사람을 발견했다.
이사람이 말하기를 빌드가 실패하는 이유중에 하나가 .gradle 폴더가 :c\user\사용자이름
아래에 존재하는데 만약 사용자 이름이 한글로 되어있을경우, 실패할수있다고 한다.
오마이갓.
그래서 부랴부랴 사용자 이름을 영어로 바꾸는법을 찾아서 했다.
관리자계정으로 바꾸고 뭐어쩌구 해서 여차저차 바꾸고 나니, 빌드가 성공했다.
아...너무 기뻤다.
원래 이렇게 했으면 build/docs 디렉토리가 생성되어야 하는데 안되는것이다.
그래서 테스트를 다시 돌려보니깐 아까까진 되던게 몇개가 또 오류가 발생해서 안되는것이었다. 그래서 바로 뚝딱뚝딱 고치고 성공 한다음에 다시 빌드해보니깐 html파일들이 생성되었다.
이때까지만 해도 index.html로 깔끔하게 한개의 문서로 정리되어서 도출될줄 알았는데, 그게 아니었다. index.adoc파일을 먼저 내가 만들어주고 그걸 토대로 테스트에 필요한 response, request, param 등 html파일들을 넣어서 index,html 파일을 만들어주는 그런 시스템이었다.
그래서 또 부랴부랴 index.adoc파일을 만들어줬다.
= API Documentation
:doctype: book
:toc: left
:toclevels: 3
:sectnums:
== 카테고리 API
=== 1. 카테고리 생성
이 API는 새로운 카테고리를 생성합니다.
==== Request
include::{snippets}/category/create/request-fields.adoc[]
==== Response
include::{snippets}/category/create/response-body.adoc[]
==== Request Example
include::{snippets}/category/create/http-request.adoc[]
...
=== 6. 카테고리 검색
이 API는 특정 이름으로 카테고리를 검색합니다.
==== Query Parameters
include::{snippets}/category/search/query-parameters.adoc[]
...
이렇게 하고 build.gradle에서
asciidoctor {
dependsOn test
configurations 'asciidoctorExt'
sourceDir = file('src/docs/asciidoc') // 소스 디렉토리 지정
outputDir = file("build/docs/asciidoc") // 출력 디렉토리 지정
inputs.dir snippetsDir // 스니펫 디렉토리
}
이부분으로 이제 내가만든 index.adoc파일이있는 곳을 sourctDir로 정하고 출력 파일을 어디로 나오게 할건지 명시해주고 다시 빌드하니
이렇게 예쁘게 나온것을 볼 수 있었다. 하루종일 눈물의 똥꼬쇼하느라 정말 힘들었다.
자괴감도 들었다. 내가 이것밖에 안되나.. 싶었다.
내일은 다른 컨트롤러들도 다 테스트케이스를 만들고 rest docs에 추가해야 겠다.
다시는 이걸로 똥꼬쇼 하지말자...!