본문 바로가기

Dev/Spring Boot

JUnit4 기반 Spring Rest Docs 작성 방법

1. gradle 세팅

dependencies {
    // asciidoc
    asciidoctor("org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.2.RELEASE")
    testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc:2.0.2.RELEASE")
    testImplementation("com.jayway.jsonpath:json-path")
}

ext {
    // Snippet 의 생성 위치를 지정
    snippetsDir = file('build/generated-snippets')
}
test {
    outputs.dir snippetsDir
    
    filter {
        includeTestsMatching '*UserControllerTest.*Group'
    }
}

asciidoctor {
    attributes 'snippets': snippetsDir
    inputs.dir snippetsDir
    dependsOn test
}

 

2. 테스트 코드 세팅

1) @Rule 을 이용하여 JUnitRestDocumentation 필드를 선언한다.

2) @Before MockMvc 생성 시 documentConfiguration() 메소드를 이용하여 apply 한다.

@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class})
public class BoraUserControllerUnitTest {

    @Rule
    public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); 

    private MockMvc mvc;

    @MockBean
    private BoraUserService boraUserService;

  
    @Autowired
    private ObjectMapper objectMapper;
    
    @Before
    public void beforeEach() {
        this.mvc = MockMvcBuilders
                .standaloneSetup(new BoraUserController(boraUserService))
                .addFilters(new CharacterEncodingFilter("UTF-8", true))
                .apply(documentationConfiguration(restDocumentation))
                .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
                .build();
    }
    
}

 

3. 컨트롤러 단위 테스트 및 스니펫 작성

1) when(서비스 메소드 호출).thenReturn(서비스 메소드 리턴 Mock 값)

2) mockMvc.perform() 과 .andExpect() 검증

3) 스니펫 작성 : document("<문서를 저장할 패키지 이름>", 요청 형식화, 응답 형식화, a, b, c))

- prettyPrint() 는 요청이나 응답 시 내용을 쉽게 읽을 수 있도록 형식화한다.

- a : pathParameter() 는 /build/generated-snippets/<테스트코드-클래스명>/<테스트코드-메소드명>/path-parameters.adoc 으로 생성된다.

- b : requestFields() 는 requestBody 에 담긴 객체들의 필드를 문서에 적는다. /build/generated-snippets/<테스트코드-클래스명>/<테스트코드-메소드명>/request-body.adoc 으로 생성된다.

- c : responseFields() 는 responseBody 에 담긴 필드를 문서에 적는다. /build/generated-snippets/<테스트코드-클래스명>/<테스트코드-메소드명>/response-body.adoc 으로 생성된다.

@Test
    public void listRole() throws Exception {
    
        // 실제 서비스를 호출 하지않고 정의된 결과를 호출
        ResultMessage resultMessage = new ResultMessage();

        List<Map<String, Object>> roleList = new LinkedList<>();
        Map<String, Object> roleInfo = new LinkedHashMap<>();
        roleInfo.put("idx", "1");
        roleInfo.put("roleName", "role");
        roleList.add(roleInfo);

		resultMessage.setData(roleList);

        Mockito.when(boraUserService.listRole()).thenReturn(resultMessage.success());

        this.mvc.perform(get("/v2/user/list_role/"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.status").value(true))
                .andDo(print());

        document("/{class-name}/{method-name}",
                        // getDocumentRequest(),
                        // getDocumentResponse(),
                        reprocessRequest(prettyPrint()),
						preprocessResponse(prettyPrint()),
                        requestParameters(
                                parameterWithName("searchCount").description("검색 갯수"),
                                parameterWithName("searchPage").description("검색 페이지")
                        ),
                responseFields(
                        fieldWithPath("data").description("사용자 리스트."),
                        fieldWithPath("status").description("응답 상태."),
                        fieldWithPath("message").description("요청 응답에 대한 메세지."),
                        fieldWithPath("data[].idx").description("권한 고유 번호."),
                        fieldWithPath("data[].roleName").description("권한명.")
                ));


    }

 

4. 결과

<http-request.adoc>

[source,http,options="nowrap"]
----
GET /v2/user/list_role/ HTTP/1.1
Host: localhost:8080

----

 

<http-response.adoc>

[source,http,options="nowrap"]
----
HTTP/1.1 200 OK
Content-Length: 116
Content-Type: application/json;charset=UTF-8

{
  "message" : "success",
  "status" : true,
  "data" : [ {
    "idx" : "1",
    "roleName" : "role"
  } ]
}
----

 

<request-body.adoc>

[source,options="nowrap"]
----

----

 

<response-body.adoc>

[source,options="nowrap"]
----
{
  "message" : "success",
  "status" : true,
  "data" : [ {
    "idx" : "1",
    "roleName" : "role"
  } ]
}
----

 

 

참고 :

https://bit.ly/3L0VJaz

https://bit.ly/3JpCabv