API 개발을 완료하고, 엔드포인트를 공유해주기 위해 스펙을 정의한다.
- 해당 스펙을 정의하는 툴에는 여러가지가 있지만 그 중 하나가Spring Rest Docs 이다.
Spring REST Docs
- 테스트 코드를 통한 API 문서 자동화 도구
- API 명세를 문서로 만들고 외부에 제공함으로써 협업을 원할하게 한다.
- 기본적으로 AsciiDoc을 사용하여 문서를 작성한다.
REST Docs 와 Swagger 를 비교
REST Docs
- 장점
- 테스트를 통과해야 문서가 만들어진다. (신뢰도가 높다)
- 프로덕션 코드에 비침투적이다.
- 단점
- 코드 양이 많다,
- 설정이 어렵다.
Swagger
- 장점
- 적용이 쉽다.
- 문서에서 바로 API 호출을 수행해 볼 수 있다.
- 단점
- 프로덕션 코드에 침투적이다.
- 테스트와 무관하기 때문에 신뢰도가 떨어질 수 있다.
REST Docs 작성해보기
- REST Docs를 작성할 때 MockMvc를 사용한다. 보통은 MockMvc를 자주 사용하고 Rest Assured 라는 테스트 도구도 존재한다.
@ExtendWith(RestDocumentationExtension.class)
public abstract class RestDocsSupport {
protected MockMvc mockMvc;
protected ObjectMapper objectMapper = new ObjectMapper();
//스프링 의존성이 없어서 new로 만들어주기
@BeforeEach
void setUp(RestDocumentationContextProvider provider) {
this.mockMvc = MockMvcBuilders.standaloneSetup(initController())
.apply(documentationConfiguration(provider))
.build();
}
protected abstract Object initController();
}
- standaloneSetup안에는 webApplicationContext 가 들어간다. 하지만, 해당 테스트에서 springboot 가 없다.
- 그렇기 때문에 바로 컨트롤러를 넣어버리자! 내가 문서로 만들고자 하는 컨트롤러를 넣으면 된다.
- 하지만...컨트롤러가 엄청나게 많을 텐데?
-> 모든 컨트롤러를 명시하기는 어려우니, 추상 클래스를 만들어서 하위 클래서에서 그 추상을 작성하도록 하자.
public class ProductControllerDocsTest extends RestDocsSupport {
private final ProductService productService = mock(ProductService.class);
@Override
protected Object initController() {
return new ProductController(productService);
}
@DisplayName("신규 상품을 등록하는 API")
@Test
void createProduct() throws Exception {
ProductCreateRequest request = ProductCreateRequest.builder()
.type(ProductType.HANDMADE)
.sellingStatus(ProdectSellingStatus.SELLING)
.name("아메리카노")
.price(4000)
.build();
//머킹에 대한 스터빙
given(productService.createdProduct(any(ProductCreateServiceRequest.class)))
.willReturn(ProductResponse.builder()
.id(1L)
.productNumber("001")
.type(ProductType.HANDMADE)
.sellingStatus(ProdectSellingStatus.SELLING)
.name("아메리카노")
.price(4000)
.build()
);
mockMvc.perform(
post("/api/v1/product/new")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isOk())
.andDo(document("product-create",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
requestFields(
fieldWithPath("type").type(JsonFieldType.STRING)
.description("상품 타입"),
fieldWithPath("sellingStatus").type(JsonFieldType.STRING)
.optional()
.description("상품 판매상태"),
fieldWithPath("name").type(JsonFieldType.STRING)
.description("상품 이름"),
fieldWithPath("price").type(JsonFieldType.NUMBER)
.description("상품 가격")
),
responseFields(
fieldWithPath("code").type(JsonFieldType.NUMBER)
.description("코드"),
fieldWithPath("status").type(JsonFieldType.STRING)
.description("상태"),
fieldWithPath("message").type(JsonFieldType.STRING)
.description("메세지"),
fieldWithPath("data").type(JsonFieldType.OBJECT)
.description("응답데이터"),
fieldWithPath("data.id").type(JsonFieldType.NUMBER)
.description("상품 ID"),
fieldWithPath("data.productNumber").type(JsonFieldType.STRING)
.description("상품 번호"),
fieldWithPath("data.type").type(JsonFieldType.STRING)
.description("상품 타입"),
fieldWithPath("data.sellingStatus").type(JsonFieldType.STRING)
.description("상품 판매상태"),
fieldWithPath("data.name").type(JsonFieldType.STRING)
.description("상품 판매상태"),
fieldWithPath("data.price").type(JsonFieldType.NUMBER)
.description("상품 가격")
)
));
}
}
- RestDocsSupport를 상속받아서 ProductControllerDocsTest를 작성한다.
- 서비스를 stubbing 하고 .andDo(document("product-create",requestFields(),responseFields() //생략) 값을 들 명시해서 문서를 만들어준다.
AsciiDoc을 생성한다.
src/docs/asciidoc/index.adoc 파일을 만들어주자
ifndef::snippets[]
:snippets: ../../build/generated-snippets //스니펫 위치 지정
endif::[]
= CafeKiosk REST API 문서 //제목장성
:doctype: book //어떤 타입인즈
:icons: font
:source-highlighter: highlightjs //소스에 하이라이트
:toc: left //목차 왼쪽
:toclevels: 2 //목차 딥스
:sectlinks: //섹션에 관한 링크
[[product-create]]
=== 판매 가능한 상품 조회
==== HTTP Request
[[Product-API]]
== Product API
include::api/product/product.adoc[]
[[product-create]]
=== 신규 상품 등록
==== HTTP Request
include::{snippets}/product-create/http-request.adoc[]
include::{snippets}/product-create/request-fields.adoc[] //위치로부터 링크
==== HTTP Response
include::{snippets}/product-create/http-response.adoc[]
include::{snippets}/product-create/response-fields.adoc[] //위치로부터 링크
- 이렇게 API 문서가 작성되게 된다.
출처 ; https://www.inflearn.com/course/practical-testing-실용적인-테스트-가이드
'TDD' 카테고리의 다른 글
값이나 환경을 바꿔가며 테스트 하고 싶을 때 (0) | 2024.12.08 |
---|---|
더 나은 테스트를 작성하기 위한 고민 (0) | 2024.12.01 |
Mockito로 Stubbing 하기 (1) | 2024.12.01 |
응답 예외처리와 책임 분리 (0) | 2024.11.25 |
Presentation Layer의 테스트 (0) | 2024.11.24 |