From 0594c83fcde91d564ea15146ae6aa0533a3df59d Mon Sep 17 00:00:00 2001 From: silver Date: Thu, 13 Apr 2023 02:25:10 +0900 Subject: [PATCH] =?UTF-8?q?[merge]=20=EC=8A=A4=ED=94=84=EB=A7=81=20?= =?UTF-8?q?=EB=A0=88=EC=8A=A4=ED=8A=B8=EB=8F=85=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B8=8C=EB=9E=9C=EC=B9=98=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat/Spring-Rest-Docs-설정-#98 --- wingle/build.gradle | 43 ++++++++++++----- .../asciidoc/community/forum/forumApi.adoc | 11 +++++ wingle/src/docs/asciidoc/index.adoc | 9 ++++ .../wingle/common/config/SecurityConfig.java | 2 +- .../community/forum/ForumControllerTest.java | 47 +++++++----------- .../kr/co/wingle/util/ControllerTest.java | 26 ++++++++++ .../kr/co/wingle/util/RestDocsConfig.java | 31 ++++++++++++ .../co/wingle/util/RestDocsTestSupport.java | 48 +++++++++++++++++++ .../restdocs/templates/request-fields.snippet | 11 +++++ .../templates/request-parameters.snippet | 9 ++++ .../templates/response-fields.snippet | 10 ++++ 11 files changed, 204 insertions(+), 43 deletions(-) create mode 100644 wingle/src/docs/asciidoc/community/forum/forumApi.adoc create mode 100644 wingle/src/docs/asciidoc/index.adoc create mode 100644 wingle/src/test/java/kr/co/wingle/util/ControllerTest.java create mode 100644 wingle/src/test/java/kr/co/wingle/util/RestDocsConfig.java create mode 100644 wingle/src/test/java/kr/co/wingle/util/RestDocsTestSupport.java create mode 100644 wingle/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet create mode 100644 wingle/src/test/resources/org/springframework/restdocs/templates/request-parameters.snippet create mode 100644 wingle/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet diff --git a/wingle/build.gradle b/wingle/build.gradle index 0587cd31..023c054d 100644 --- a/wingle/build.gradle +++ b/wingle/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '2.7.7' id 'io.spring.dependency-management' version '1.0.15.RELEASE' - id 'org.asciidoctor.convert' version '1.5.8' + id "org.asciidoctor.jvm.convert" version "3.3.2" id 'jacoco' } @@ -14,8 +14,8 @@ repositories { mavenCentral() } -ext { - set('snippetsDir', file("build/generated-snippets")) +configurations { + asciidoctorExt } dependencies { @@ -49,25 +49,20 @@ dependencies { implementation 'org.mapstruct:mapstruct:1.5.3.Final' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' + + // spring rest docs + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' } tasks.named('test') { - outputs.dir snippetsDir useJUnitPlatform() } -tasks.named('asciidoctor') { - inputs.dir snippetsDir - dependsOn test -} - jacoco { toolVersion = '0.8.8' } -test { - finalizedBy 'jacocoTestReport' -} jacocoTestReport { dependsOn test @@ -126,3 +121,27 @@ jacocoTestCoverageVerification { jar { enabled = false } + + +ext { + snippetsDir = file('build/generated-snippets') +} + +test { + finalizedBy 'jacocoTestReport' + outputs.dir snippetsDir +} + +asciidoctor { + inputs.dir snippetsDir + configurations 'asciidoctorExt' + dependsOn test +} + +bootJar { + // package the generated documentation + dependsOn asciidoctor + from("${asciidoctor.outputDir}") { + into 'static/docs' + } +} diff --git a/wingle/src/docs/asciidoc/community/forum/forumApi.adoc b/wingle/src/docs/asciidoc/community/forum/forumApi.adoc new file mode 100644 index 00000000..888adebf --- /dev/null +++ b/wingle/src/docs/asciidoc/community/forum/forumApi.adoc @@ -0,0 +1,11 @@ +[[Forum-API]] +== Forum API + +[[게시판-불러오기]] +=== 게시판 불러오기 + +operation::forum-controller-test/게시판_목록_조회[snippets=http-request,path-parameters,request-fields,request-body] + +==== 성공 + +operation::forum-controller-test/게시판_목록_조회[snippets='response-fields-data,response-body'] diff --git a/wingle/src/docs/asciidoc/index.adoc b/wingle/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000..fd28a002 --- /dev/null +++ b/wingle/src/docs/asciidoc/index.adoc @@ -0,0 +1,9 @@ += REST Docs +:asciidoc: src/docs/asciidoc +:toc: left +:toclevels: 2 +:source-highlighter: highlightjs + +include::{asciidoc}/community/forum/forumApi.adoc[] + + diff --git a/wingle/src/main/java/kr/co/wingle/common/config/SecurityConfig.java b/wingle/src/main/java/kr/co/wingle/common/config/SecurityConfig.java index ec291158..7a41cc4e 100644 --- a/wingle/src/main/java/kr/co/wingle/common/config/SecurityConfig.java +++ b/wingle/src/main/java/kr/co/wingle/common/config/SecurityConfig.java @@ -48,7 +48,7 @@ protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .and() .authorizeRequests() .antMatchers("/", "/api/v1/auth/**", "/api/v1/admin/**", "/api/v1/community/**", "/api/v1/messages/**", - "/api/v1/profile/**").permitAll() + "/api/v1/profile/**", "/docs/**").permitAll() .anyRequest().authenticated() .and() diff --git a/wingle/src/test/java/kr/co/wingle/community/forum/ForumControllerTest.java b/wingle/src/test/java/kr/co/wingle/community/forum/ForumControllerTest.java index 6c3cddae..c21b658f 100644 --- a/wingle/src/test/java/kr/co/wingle/community/forum/ForumControllerTest.java +++ b/wingle/src/test/java/kr/co/wingle/community/forum/ForumControllerTest.java @@ -1,49 +1,37 @@ package kr.co.wingle.community.forum; +import static kr.co.wingle.util.RestDocsConfig.*; import static org.mockito.BDDMockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.web.servlet.MockMvc; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.filter.CharacterEncodingFilter; - -import com.fasterxml.jackson.databind.ObjectMapper; import kr.co.wingle.common.constants.SuccessCode; import kr.co.wingle.common.dto.ApiResponse; +import kr.co.wingle.util.RestDocsTestSupport; -@ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) -@AutoConfigureMockMvc -public class ForumControllerTest { - private MockMvc mockMvc; - +public class ForumControllerTest extends RestDocsTestSupport { @MockBean private ForumService forumService; - ObjectMapper mapper = new ObjectMapper(); - - @BeforeEach - public void setup(WebApplicationContext webApplicationContext) { - this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) - .addFilters(new CharacterEncodingFilter("UTF-8", true)) - .alwaysDo(print()) - .build(); + protected FieldDescriptor[] forumData() { + return new FieldDescriptor[] { + fieldWithPath("id").type(JsonFieldType.NUMBER) + .description("게시판 id") + .attributes(field("constraints", "양수 정수")), + fieldWithPath("name").type(JsonFieldType.STRING) + .description("게시판 이름") + }; } @Test @@ -55,7 +43,7 @@ public void setup(WebApplicationContext webApplicationContext) { given(forumService.findAll()) .willReturn(responseDto); - MockHttpServletRequestBuilder builder = get("/api/v1/community/forums") + MockHttpServletRequestBuilder builder = RestDocumentationRequestBuilders.get("/api/v1/community/forums") .contentType(MediaType.APPLICATION_JSON); mockMvc.perform(builder) @@ -63,7 +51,6 @@ public void setup(WebApplicationContext webApplicationContext) { .andExpect(content().string( mapper.writeValueAsString(ApiResponse.success(SuccessCode.GET_SUCCESS, responseDto)) )) - .andDo(print()); + .andDo(getResponseFields(forumData())); } - } diff --git a/wingle/src/test/java/kr/co/wingle/util/ControllerTest.java b/wingle/src/test/java/kr/co/wingle/util/ControllerTest.java new file mode 100644 index 00000000..22709fa6 --- /dev/null +++ b/wingle/src/test/java/kr/co/wingle/util/ControllerTest.java @@ -0,0 +1,26 @@ +package kr.co.wingle.util; + +import org.junit.jupiter.api.Disabled; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Disabled +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) +@AutoConfigureMockMvc +public abstract class ControllerTest { + + @Autowired + protected ObjectMapper mapper; + + @Autowired + protected MockMvc mockMvc; + + protected String createJson(Object dto) throws JsonProcessingException { + return mapper.writeValueAsString(dto); + } +} diff --git a/wingle/src/test/java/kr/co/wingle/util/RestDocsConfig.java b/wingle/src/test/java/kr/co/wingle/util/RestDocsConfig.java new file mode 100644 index 00000000..52aad764 --- /dev/null +++ b/wingle/src/test/java/kr/co/wingle/util/RestDocsConfig.java @@ -0,0 +1,31 @@ +package kr.co.wingle.util; + +import static org.springframework.restdocs.snippet.Attributes.*; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.operation.preprocess.Preprocessors; + +@TestConfiguration +public class RestDocsConfig { + + @Bean + public RestDocumentationResultHandler write() { + return MockMvcRestDocumentation.document( + // 조각이 생성되는 디렉토리 명을 클래스명/메서드 명으로 정함 + "{class-name}/{method-name}", + // json pretty하게 출력 + Preprocessors.preprocessRequest(Preprocessors.prettyPrint()), + Preprocessors.preprocessResponse(Preprocessors.prettyPrint()) + ); + } + + // 제약조건 + public static final Attribute field( + final String key, + final String value) { + return new Attribute(key, value); + } +} diff --git a/wingle/src/test/java/kr/co/wingle/util/RestDocsTestSupport.java b/wingle/src/test/java/kr/co/wingle/util/RestDocsTestSupport.java new file mode 100644 index 00000000..9541fda6 --- /dev/null +++ b/wingle/src/test/java/kr/co/wingle/util/RestDocsTestSupport.java @@ -0,0 +1,48 @@ +package kr.co.wingle.util; + +import static org.springframework.restdocs.payload.PayloadDocumentation.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Import; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.filter.CharacterEncodingFilter; + +@Disabled +@Import(RestDocsConfig.class) +@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) +public class RestDocsTestSupport extends ControllerTest { + + @Autowired + protected RestDocumentationResultHandler restDocs; + + @BeforeEach + void setUp(final WebApplicationContext context, + final RestDocumentationContextProvider provider) { + this.mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(MockMvcRestDocumentation.documentationConfiguration(provider)) // rest docs 설정 주입 + .alwaysDo(MockMvcResultHandlers.print()) // andDo(print()) 코드 포함 + .alwaysDo(restDocs) // pretty 패턴과 문서 디렉토리 명 정해준것 적용 + .addFilters(new CharacterEncodingFilter("UTF-8", true)) // 한글 깨짐 방지 + .build(); + } + + protected RestDocumentationResultHandler getResponseFields(FieldDescriptor[] data) { + return restDocs.document( + responseFields(beneathPath("data") // response의 data 필드 하위 내용 문서화 + .withSubsectionId("data") // response-fields-data.adoc 파일 생성 + , data) + ); + } + +} \ No newline at end of file diff --git a/wingle/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet b/wingle/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet new file mode 100644 index 00000000..8e7b90bb --- /dev/null +++ b/wingle/src/test/resources/org/springframework/restdocs/templates/request-fields.snippet @@ -0,0 +1,11 @@ +//@formatter:off +|=== +|필드명|타입|필수여부|제약조건|설명 +{{#fields}} +|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} +|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} +|{{#tableCellContent}}{{^optional}}true{{/optional}}{{#optional}}false{{/optional}}{{/tableCellContent}} +|{{#tableCellContent}}{{#constraints}}{{.}}{{/constraints}}{{/tableCellContent}} +|{{#tableCellContent}}{{description}}{{/tableCellContent}} +{{/fields}} +|=== \ No newline at end of file diff --git a/wingle/src/test/resources/org/springframework/restdocs/templates/request-parameters.snippet b/wingle/src/test/resources/org/springframework/restdocs/templates/request-parameters.snippet new file mode 100644 index 00000000..623aef91 --- /dev/null +++ b/wingle/src/test/resources/org/springframework/restdocs/templates/request-parameters.snippet @@ -0,0 +1,9 @@ +//@formatter:off +|=== +|파라미터|필수여부|설명 +{{#parameters}} +|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} +|{{#tableCellContent}}{{^optional}}true{{/optional}}{{#optional}}false{{/optional}}{{/tableCellContent}} +|{{#tableCellContent}}{{description}}{{/tableCellContent}} +{{/parameters}} +|=== \ No newline at end of file diff --git a/wingle/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet b/wingle/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet new file mode 100644 index 00000000..119e9c44 --- /dev/null +++ b/wingle/src/test/resources/org/springframework/restdocs/templates/response-fields.snippet @@ -0,0 +1,10 @@ +//@formatter:off +|=== +|필드명|타입|필수여부|설명 +{{#fields}} +|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} +|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} +|{{#tableCellContent}}{{^optional}}true{{/optional}}{{#optional}}false{{/optional}}{{/tableCellContent}} +|{{#tableCellContent}}{{description}}{{/tableCellContent}} +{{/fields}} +|=== \ No newline at end of file