이 가이드는 Spring 애플리케이션에서 HTTP 엔드포인트에 대한 문서를 생성하는 프로세스를 안내합니다.
API를 노출하는 일부 HTTP 엔드포인트를 사용하여 간단한 Spring 애플리케이션을 구축합니다. JUnit과 Spring의 MockMvc
를 사용하여 웹 계층만 테스트합니다. 그런 다음 동일한 테스트를 사용하여 Spring REST Docs를 사용하여 API에 대한 문서를 생성합니다.
Spring 애플리케이션을 위한 새 컨트롤러를 만듭니다. 다음 목록(src/main/java/guides/testingrestdocs/HomeController.java)에서는 이를 수행하는 방법을 보여줍니다.
package guides.testingrestdocs;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.Map;
@RestController
public class HomeController {
@GetMapping("/")
public Map<String, Object> greeting() {
return Collections.singletonMap("message", "Hello, World");
}
}
Spring Initializr는 애플리케이션을 시작하는 데 사용할 수 있는 기본 클래스를 생성합니다. 다음 목록(src/main/java/guides/testingrestdocs/TestingRestdocsApplication.java)은 Spring Initializr가 생성한 애플리케이션 클래스를 보여줍니다.
package guides.testingrestdocs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TestingRestdocsApplication {
public static void main(String[] args) {
SpringApplication.run(TestingRestdocsApplication.class, args);
}
}
이제 애플리케이션이 실행되었으므로 테스트할 수 있습니다. http://localhost:8080
에서 홈 페이지를 로드할 수 있습니다. 그러나 변경 사항을 적용할 때 애플리케이션이 작동한다는 확신을 더 가지려면 테스트를 자동화하려고 합니다. 또한 HTTP 끝점(endpoint)에 대한 설명서를 게시하려고 합니다. Spring REST Docs를 사용하여 테스트의 일부로 해당 테스트의 동적 부분을 생성할 수 있습니다.
가장 먼저 할 수 있는 일은 애플리케이션 컨텍스트를 시작할 수 없는 경우 실패하는 간단한 온전성 검사 테스트(sanity check)를 작성하는 것입니다. 이렇게 하려면 테스트 범위에서 프로젝트에 대한 종속성으로 Spring Test 및 Spring REST Docs를 추가하세요.
다음 목록은 Gradle을 사용하는 경우 추가할 항목을 보여줍니다.
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.1'
id 'io.spring.dependency-management' version '1.1.4'
id 'org.asciidoctor.jvm.convert' version '3.3.2'
}
group = 'guides'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
ext {
set('snippetsDir', file("build/generated-snippets"))
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}
tasks.named('asciidoctor') {
inputs.dir snippetsDir
dependsOn test
}
ext { set('snippetsDir', file("build/generated-snippets")) }
:
ext
블록은 Gradle 빌드 스크립트의 확장 속성을 정의하는 곳입니다.snippetsDir
이라는 이름의 확장 속성을 설정하고, 해당 디렉토리를 build/generated-snippets
로 지정합니다.tasks.named('test') { outputs.dir snippetsDir useJUnitPlatform() }
:
test
task에 대한 설정입니다.outputs.dir snippetsDir
는 snippetsDir
디렉토리가 test
task의 출력 디렉토리로 지정됨을 나타냅니다.useJUnitPlatform()
은 JUnit 테스트를 실행하기 위해 JUnit Platform을 사용하도록 설정합니다.tasks.named('asciidoctor') { inputs.dir snippetsDir dependsOn test }
:
asciidoctor
task에 대한 설정입니다.inputs.dir snippetsDir
는 snippetsDir
디렉토리를 asciidoctor
task의 입력 디렉토리로 설정합니다.dependsOn test
는 asciidoctor
task가 test
task에 의존한다는 것을 나타냅니다. 즉, asciidoctor
task가 실행되기 전에 test
task가 먼저 실행됩니다.asciidoctor { sourceDir 'src/main/asciidoc' attributes('snippets': file('target/snippets')) }
:
asciidoctor
블록은 Asciidoctor 플러그인의 설정을 담고 있습니다.sourceDir 'src/main/asciidoc'
는 Asciidoctor 플러그인이 AsciiDoc 파일을 찾을 디렉토리를 설정합니다.attributes('snippets': file('target/snippets'))
는 Asciidoctor 속성 중 하나로, AsciiDoc 파일에서 {snippets}
를 사용할 때 이를 target/snippets
디렉토리로 치환하도록 설정합니다.빌드 파일의 주석은 무시해도 됩니다. 이 가이드에 포함할 파일의 일부를 선택할 수 있도록 도와줍니다.
Spring MockMvc를 사용하여 HTTP 콘텐츠를 캡처하는 REST Docs의
mockmvc
버전을 포함했습니다. 자신의 애플리케이션이 Spring MVC를 사용하지 않는 경우 전체 스택 통합 테스트와 함께 작동하는restassured
버전을 사용할 수도 있습니다.
이제 다음 예제(src/test/java/guides/testingrestdocs/TestingRestdocsApplicationTests.java)에서 볼 수 있듯이 @RunWith
및 @SpringBootTest
주석과 빈(empty) 테스트 메서드를 사용하여 테스트 사례를 만듭니다.
package guides.testingrestdocs;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TestingRestdocsApplicationTests {
@Test
void contextLoads() throws Exception{
}
}
온전한지 확인하는 것은 좋지만 애플리케이션의 동작을 확인하는 몇 가지 테스트도 작성해야 합니다. 유용한 접근 방식은 Spring이 들어오는 HTTP 요청을 처리하고 이를 컨트롤러에 전달하는 MVC 계층만 테스트하는 것입니다. 이를 위해 Spring의 MockMvc
를 사용하고 테스트 케이스의 @WebMvcTest
주석을 사용하여 주입을 요청할 수 있습니다. 다음 예제(src/test/java/guides/testingrestdocs/WebLayerTest.java)에서는 이를 수행하는 방법을 보여줍니다.
package guides.testingrestdocs;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class) //@RunWith(SpringRunner.class) JUnit 4
@WebMvcTest(HomeController.class)
public class WebLayerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string(containsString("Hello, World")));
}
}
이전 섹션의 테스트는 HTTP 요청을 만들고(모의) 응답을 어설션합니다. 여러분이 만든 HTTP API에는 (적어도 원칙적으로는) 동적 콘텐츠가 있으므로 테스트를 감시하고 문서에 사용하기 위해 HTTP 요청을 빼낼 수 있다면 정말 좋을 것입니다. Spring REST Docs를 사용하면 "스니펫"을 생성하여 이를 수행할 수 있습니다. 테스트에 주석과 추가 "어설션"을 추가하여 이 작업을 수행할 수 있습니다. 다음 예제(src/test/java/guides/testingrestdocs/WebLayerTest.java)는 전체 테스트를 보여줍니다.
package guides.testingrestdocs;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(HomeController.class)
@AutoConfigureRestDocs(outputDir = "target/snippets")
public class WebLayerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()).andExpect(content().string(containsString("Hello, World"))).andDo(document("home"));
}
}
새로운 주석은 @AutoConfigureRestDocs
(Spring Boot의)이며 생성된 조각의 디렉터리 위치에 대한 인수를 사용합니다. 그리고 새로운 주장은 조각의 문자열 식별자에 대한 인수를 취하는 MockMvcRestDocumentation.document
입니다.
Gradle 사용자는 출력(output) 디렉터리로
target
대신build
를 사용하는 것을 선호할 수 있습니다. 그러나 그것은 중요하지 않습니다. 원하는 것을 사용하십시오.
테스트를 실행한 다음 target/snippet
을 살펴보세요. 다음과 같이 Asciidoctor 조각이 포함된 home
(식별자)이라는 디렉터리를 찾아야 합니다.
└── target
└── snippets
└── home
└── curl-request.adoc
└── http-request.adoc
└── http-response.adoc
└── httpie-request.adoc
└── request-body.adoc
└── response-body.adoc
기본 조각은 HTTP 요청 및 응답에 대한 Asciidoctor 형식입니다. 또한 curl
및 httpie
(두 가지 일반적이고 널리 사용되는 명령줄 HTTP 클라이언트)에 대한 명령줄 예제도 있습니다.
테스트에서 document()
어설션에 인수를 추가하여 추가 조각을 생성할 수 있습니다. 예를 들어 다음 예제(src/test/java/guides/testingrestdocs/WebLayerTest.java에서)에 표시된 것처럼 PayloadDocumentation.responseFields()
조각을 사용하여 JSON 응답의 각 필드를 문서화할 수 있습니다.
this.mockMvc.perform(get("/"))
...
.andDo(document("home", responseFields(
fieldWithPath("message").description("The welcome message for the user.")
));
테스트를 실행하면 response-fields.adoc
라는 추가 조각 파일을 찾아야 합니다. 여기에는 필드 이름과 설명이 포함된 테이블이 포함되어 있습니다. 필드를 생략하거나 이름이 잘못되면 테스트가 실패합니다. 이것이 REST Docs의 힘입니다.
사용자 정의 스니펫을 생성하고 스니펫의 형식을 변경하며 호스트 이름과 같은 값을 사용자 정의할 수 있습니다. 자세한 내용은 Spring REST Docs 문서를 참조하세요.
생성된 코드 조각을 사용하려면 프로젝트에 Asciidoctor 콘텐츠를 일부 포함하고 빌드 시 코드 조각을 포함해야 합니다. 이 작업을 보려면 src/main/asciidoc/index.adoc
라는 새 파일을 만들고 원하는 대로 스니펫을 포함하세요. 다음 예제(src/main/asciidoc/index.adoc
)에서는 이를 수행하는 방법을 보여줍니다.
= Getting Started With Spring REST Docs
This is an example output for a service running at http://localhost:8080:
.request
include::{snippets}/home/http-request.adoc[]
.response
include::{snippets}/home/http-response.adoc[]
As you can see the format is very simple, and in fact you always get the same message.
이 Asciidoc 파일의 주요 특징은 Asciidoctor include
지시문(directive)을 사용하여 두 개의 스니펫을 포함한다는 것입니다(콜론과 후행 괄호는 파서에게 해당 행에 대해 특별한 작업을 수행하도록 지시합니다). 포함된 조각에 대한 경로는 {snippets}
라는 자리 표시자(Asciidoctor의 attribute
)로 표현됩니다. 이 간단한 경우에 유일한 다른 마크업은 상단의 =
(레벨 1 섹션 제목)와 스니펫의 캡션('요청' 및 '응답') 앞에.
입니다..
는 해당 줄의 텍스트를 캡션으로 바꿉니다.
그런 다음 빌드 구성에서 이 소스 파일을 선택한 문서 형식으로 처리해야 합니다.
Gradle을 사용하는 경우 ./gradlew asciidoctor
를 실행하면 build/asciidoc
이 생성됩니다. 다음 목록은 build.gradle
파일의 Asciidoctor 관련 부분을 보여줍니다.
index.adoc
의 include 문에서 {snippets}
는 build.gradle
에 의해 build/generated-snippets
로 지정되고 있었습니다.
// 중략
ext {
set('snippetsDir', file("build/generated-snippets"))
}
tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}
tasks.named('asciidoctor') {
inputs.dir snippetsDir
dependsOn test
}
build 설정에 따르면 test
태스크의 출력물은 snippetsDir
로 향하고, asciidoctor
태스크의 입력물은 snippetsDir
로부터 옵니다. 그리고 이 태스크는 test
태스크를 실행한 후 수행됩니다.
그러나 튜토리얼 원문에서 @AutoConfigureRestDocs
의 outputDir
의 인수를 target/snippets
로 지정하는 바람에 build/generated-snippets
폴더로 .adoc
파일들이 모이지 못했고, 이 때문에 src/main/asciidoc/index.adoc
의 include
지시문은 제대로 작동하지 않고 있었습니다.
결국 @AutoConfigureRestDocs
의 outputDir
의 인수를 build/generated-snippets
로 수정한 후에야 제대로 된 결과를 볼 수 있었습니다.