Skip to content

Commit

Permalink
[merge] 스프링 레스트독 설정 브랜치 #110
Browse files Browse the repository at this point in the history
feat/Spring-Rest-Docs-설정-#98
  • Loading branch information
LeeJE20 committed Apr 12, 2023
1 parent 72b4917 commit 0594c83
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 43 deletions.
43 changes: 31 additions & 12 deletions wingle/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}

Expand All @@ -14,8 +14,8 @@ repositories {
mavenCentral()
}

ext {
set('snippetsDir', file("build/generated-snippets"))
configurations {
asciidoctorExt
}

dependencies {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'
}
}
11 changes: 11 additions & 0 deletions wingle/src/docs/asciidoc/community/forum/forumApi.adoc
Original file line number Diff line number Diff line change
@@ -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']
9 changes: 9 additions & 0 deletions wingle/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
= REST Docs
:asciidoc: src/docs/asciidoc
:toc: left
:toclevels: 2
:source-highlighter: highlightjs

include::{asciidoc}/community/forum/forumApi.adoc[]


Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -55,15 +43,14 @@ 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)
.andExpect(status().isOk())
.andExpect(content().string(
mapper.writeValueAsString(ApiResponse.success(SuccessCode.GET_SUCCESS, responseDto))
))
.andDo(print());
.andDo(getResponseFields(forumData()));
}

}
26 changes: 26 additions & 0 deletions wingle/src/test/java/kr/co/wingle/util/ControllerTest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
31 changes: 31 additions & 0 deletions wingle/src/test/java/kr/co/wingle/util/RestDocsConfig.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
48 changes: 48 additions & 0 deletions wingle/src/test/java/kr/co/wingle/util/RestDocsTestSupport.java
Original file line number Diff line number Diff line change
@@ -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)
);
}

}
Original file line number Diff line number Diff line change
@@ -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}}
|===
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//@formatter:off
|===
|파라미터|필수여부|설명
{{#parameters}}
|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{^optional}}true{{/optional}}{{#optional}}false{{/optional}}{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/parameters}}
|===
Original file line number Diff line number Diff line change
@@ -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}}
|===

0 comments on commit 0594c83

Please sign in to comment.