์๋น์ค ๊ตฌํ์ ์์ REST API ๋ฌธ์๋ฅผ ์๋ํ ํด์ฃผ๋ Spring REST Docs ๋ฅผ ์ฌ์ฉํด๋ณธ๋ค.
Docs๋ฅผ ์ฌ์ฉํ ์ด์ ๋ Test์์ ์ฑ๊ณตํด์ผ๋ง ๋ฌธ์๋ฅผ ๋ง๋ค ์ ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๊ฒ๋์์ต๋๋ค.
๊ฐ์ ๋ก TEST CODE๋ฅผ ์์ฑํ์ฌ API์ ๋ํ ์ ๋ขฐ์ฑ์ ๋์ผ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
Gradle 7์ ์ฌ์ฉํ๊ณ ์์๋๋ฐ ๋ฌธ์์์ฑ์ด ์๋์ ๊ฒ์ํด๋ดค๋ค.
์ฐํ๋ฐฉ๋ฒ์ด ์์ง๋ง ๋ณต์กํ๊ธฐ ๋๋ฌธ์ ๋ค์ด๊ทธ๋ ์ด๋๋ฅผ ์ ํํ๋ค.
๊ทธ๋ฆฌ๊ณ Gradle๊ณผ Maven ์์ ๋ค๋ฅด๊ฒ ์ค์ ๋๋ ๋ถ๋ถ์ด ์๊ธฐ ๋๋ฌธ์ ๋งจ์๋ ๋งํฌ๋ฅผ ํตํด ํ์ธํด๋ณด๋ ๊ฒ๋ ์ข๋ค!
ํ๊ฒฝ์ค์
Springboot 2.6.1
Gradle 6.9.1
JUnit5
Asciidoctor 1.5.9.2
build.gradle ์ ์ถ๊ฐํ ์ค์
plugins {
id "org.asciidoctor.convert" version "1.5.9.2"
}
dependencies {
asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
ext {
snippetsDir = file('build/generated-snippets')
}
test {
useJUnitPlatform()
outputs.dir snippetsDir
}
asciidoctor {
inputs.dir snippetsDir
dependsOn test
}
bootJar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/asciidoc/html5/")
into file("src/main/resources/static/docs")
}
build {
dependsOn copyDocument
}
๋ฌธ์๋ Buildํ ๋ build/asciidoc/html5/ ์ ์ ์ฅ๋๋๋ฐ src/main/resources/static/docs ์๋ ๋ณต์ฌ๊ฐ ๋์ด ๋ฐฐํฌ์
๋ฐ๋ก ํ์ธํ ์ ์๋๋ก ์ค์ ํ์ต๋๋ค.
TEST CODE ์ค์
@ExtendWith(RestDocumentationExtension.class)
@SpringBootTest
class UserRegisterControllerTest {
private MockMvc mockMvc;
@BeforeEach
public void setUp(
WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation))
.build();
}
}
์ผ๋ฐ์ ์ธ MockMvc ์ธํ ์ ์ถ๊ฐ๋ก apply(documentationConfiguration(restDocumentation)) ์ ์ถ๊ฐ ํด์ฃผ์ด ๋ฌธ์ํ๋ฅผ ํ ์ ์๋๋ก ์ค์ ํ์ต๋๋ค.
์ ์ ์์ฑ ํ ์คํธ
@Test
void create() throws Exception {
final UserRegisterResponse userRegisterResponse = UserRegisterResponse.builder().id(1L).build();
when(userRegisterService.addUser(any())).thenReturn(userRegisterResponse);
this.mockMvc
.perform(
post("/user")
.content("{\"socialToken\":\"abc\"}")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andDo(document("user-create",requestFields(
fieldWithPath("socialToken").description("์์
ํ ํฐ")
)));
}
post ๋ฉ์๋ ์ด๊ธฐ ๋๋ฌธ์ content์ ํ์ํ ํ๋ผ๋ฏธํฐ๋ฅผ json ๋ฐฉ์์ผ๋ก ์ ๋ ฅํด์ค๋๋ค.
1. user-create ๋ ์๋์ ๊ฐ์ด buildํ์ผ ๋ด์ ์๊ธฐ๋ ์ด๋ฆ์ ์ค์ ํด์ค๋๋ค.
2. fieldWithPath().description() ์ ์๋์ ๊ฐ์ด ์ถ๋ ฅํด์ค ์ ์์ต๋๋ค.
๋ง์ง๋ง์ผ๋ก src/docs/asclidoc/api-docs.adoc ๋ฅผ ๋ง๋ค์ด์ ์๋์ ๊ฐ์ด ์์ฑํฉ๋๋ค.
* api-docs ๋ http://localhost:8080/docs/api-docs.html ์ ๊ฐ์ด
html ํ์ผ์ ์ด๋ฆ์ ์ ํด์ฃผ๊ธฐ ๋๋ฌธ์ ๊ด๋ จ๋ ์ด๋ฆ์ผ๋ก ์ง์ด์ค๋๋ค.
๊ณต์๋ฌธ์ ์ฐธ๊ณ ( ๋ฒ์ญ๋ณธ๋ ์๋๋ฏ... )
https://docs.asciidoctor.org/asciidoctor/latest/
Asciidoctor - Asciidoctor Documentation
A documentation page for Asciidoctor.
docs.asciidoctor.org
= Spring REST Docs
:toc: left
:toclevels: 2
:sectlinks:
[[resources-post]]
== User
[[resources-post-create]]
=== User ์์ฑ
==== Request Fields
include::{snippets}/user-create/request-fields.adoc[]
==== HTTP request
include::{snippets}/user-create/http-request.adoc[]
==== HTTP response
include::{snippets}/user-create/http-response.adoc[]
include ์ ํด๋นํ๋ adoc๋ build/generated-snippets ์์ ํ์ธํ ์ ์์ต๋๋ค!
์๋ฒ ์คํํ
http://localhost:8080/docs/api-docs.html ๋ก ์ ์ํ๋ฉด ๋ฌธ์๊ฐ ๋ง๋ค์ด์ง๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค!
+ 2022.02.09 List ์กฐํ
List๋ก ๋ Response์ผ ๊ฒฝ์ฐ fieldWithPath๋ฅผ ์๋์ ๊ฐ์ด []. ๋ฅผ ๋ถ์ฌ์ค๋ค
@Test
void findAllWish() throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy๋
MM์ dd์ผ E์์ผ", Locale.KOREAN);
final List<WishListFindAllResponse> wishListFindAllResponse =
Lists.newArrayList(
WishListFindAllResponse.builder()
.wishListId(1L)
.complete_date(null)
.register_date(
simpleDateFormat.format(java.sql.Timestamp.valueOf(LocalDateTime.now())))
.content("๋ฐ๋ค ๊ฐ๊ธฐ")
.wishUserNickname("์ ์ ")
.status(WishStatus.INCOMPLETE)
.build());
when(wishFindService.findAllWish(any())).thenReturn(wishListFindAllResponse);
this.mockMvc
.perform(get("/wishlists/{coupleToken}", "AAAAA").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(
document(
"wish-find-all",
pathParameters(parameterWithName("coupleToken").description("์ปคํ ํ ํฐ")),
responseFields(
fieldWithPath("[].wishListId").description("์์ ๊ณ ์ ID"),
fieldWithPath("[].content").description("์์ ๋ด์ฉ"),
fieldWithPath("[].register_date").description("์์ ์์ฑ ๋ ์ง"),
fieldWithPath("[].complete_date").description("์์ ์๋ฃ ๋ ์ง"),
fieldWithPath("[].wishUserNickname").description("์์ ์์ฑํ ์ ์ ์ ๋๋ค์"),
fieldWithPath("[].status").description("์์ ์ํ"))));
}
์ฐธ๊ณ
https://tecoble.techcourse.co.kr/post/2020-08-18-spring-rest-docs/
API ๋ฌธ์ ์๋ํ - Spring REST Docs ํ์๋ณด๊ฒ ์ต๋๋ค
ํ๋ก๋์ ์ฝ๋์ ๋ถ๋ฆฌํ์ฌ ๋ฌธ์ ์๋ํ๋ฅผ ํ๊ณ ์ถ๋ค๊ณ ์? ์ ๋ขฐ๋ ๋์ API ๋ฌธ์๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค๊ณ ์? ํ ์คํธ๊ฐ ์ฑ๊ณตํด์ผ ๋ฌธ์๋ฅผ ๋ง๋ค ์ ์๋ค!! Spring REST Docs๊ฐ ์์ต๋๋ค. API ๋ฌธ์๋ฅผ ์๋ํ ๋๊ตฌ๋ก
tecoble.techcourse.co.kr
https://blog.hodory.dev/2019/12/04/spring-rest-docs-with-gradle-not-working-html5/
๏ผปJava๏ผฝSpring REST Docs HTML์ด ์์ฑ๋์ง ์์๋
๋ฐฑ๊ธฐ์ ๋์ ์คํ๋ง๋ถํธ ๊ฐ์ข๋ฅผ ์๊ฐํ๋์ค์ Spring REST Docs๋ฅผ ์ด์ฉํ์ฌ HTML์ ์์ฑํ๋ คํ๋๋ฐ,์๋ฌด๋ฆฌ ๋น๋๋ฅผ ํด๋ ascii\html\index.html์ด ์์ฑ๋์ง ์์์ต๋๋ค. 12345678910111213141516171819202122232425262728293
blog.hodory.dev
'Project > DARLING' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
7. CI/CD ๊ณํ (0) | 2022.02.09 |
---|---|
6. ๋ฌด์์ด ๋ฌธ์ ์ผ๊น ... (0) | 2022.01.25 |
4. ์ฐ๊ด๊ด๊ณ - OneToMany (0) | 2022.01.23 |
3. ์ฐ๊ด๊ด๊ณ - ManyToOne (0) | 2022.01.23 |
2. Entity (0) | 2021.12.03 |