Skip to content

Instantly share code, notes, and snippets.

@eungju
Last active July 2, 2022 14:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eungju/7ed0e5b0c5090b41f1666c3786b1ba4d to your computer and use it in GitHub Desktop.
Save eungju/7ed0e5b0c5090b41f1666c3786b1ba4d to your computer and use it in GitHub Desktop.
Spring REST Docs Kotlin DSL
import org.springframework.restdocs.headers.HeaderDescriptor
import org.springframework.restdocs.headers.HeaderDocumentation
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation
import org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse
import org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint
import org.springframework.restdocs.payload.FieldDescriptor
import org.springframework.restdocs.payload.PayloadDocumentation
import org.springframework.restdocs.payload.SubsectionDescriptor
import org.springframework.restdocs.request.ParameterDescriptor
import org.springframework.restdocs.request.RequestDocumentation
import org.springframework.restdocs.snippet.Snippet
import org.springframework.test.web.servlet.ResultActionsDsl
class DocumentScope {
internal val snippets = mutableListOf<Snippet>()
abstract class HttpMessageScope {
internal val headerDescriptors = mutableListOf<HeaderDescriptor>()
internal val fieldDescriptors = mutableListOf<FieldDescriptor>()
fun header(name: String): HeaderDescriptor =
HeaderDocumentation.headerWithName(name)
.also { headerDescriptors.add(it) }
fun field(path: String): FieldDescriptor =
PayloadDocumentation.fieldWithPath(path)
.also { fieldDescriptors.add(it) }
fun subsection(path: String): SubsectionDescriptor =
PayloadDocumentation.subsectionWithPath(path)
.also { fieldDescriptors.add(it) }
}
class RequestScope : HttpMessageScope() {
internal val parameterDescriptors = mutableListOf<ParameterDescriptor>()
fun parameter(name: String): ParameterDescriptor =
RequestDocumentation.parameterWithName(name)
.also { parameterDescriptors.add(it) }
}
fun request(configure: RequestScope.() -> Unit) {
val scope = RequestScope().apply(configure)
scope.parameterDescriptors.let {
if (it.isNotEmpty()) {
snippets.add(RequestDocumentation.requestParameters(it))
}
}
scope.headerDescriptors.let {
if (it.isNotEmpty()) {
snippets.add(HeaderDocumentation.requestHeaders(it))
}
}
scope.fieldDescriptors.let {
if (it.isNotEmpty()) {
snippets.add(PayloadDocumentation.requestFields(it))
}
}
}
class ResponseScope : HttpMessageScope()
fun response(configure: ResponseScope.() -> Unit) {
val scope = ResponseScope().apply(configure)
scope.headerDescriptors.let {
if (it.isNotEmpty()) {
snippets.add(HeaderDocumentation.responseHeaders(it))
}
}
scope.fieldDescriptors.let {
if (it.isNotEmpty()) {
snippets.add(PayloadDocumentation.responseFields(it))
}
}
}
}
fun ResultActionsDsl.andDocument(
identifier: String,
configure: DocumentScope.() -> Unit
): ResultActionsDsl =
andDo {
val scope = DocumentScope().apply(configure)
handle(
MockMvcRestDocumentation.document(
identifier,
preprocessResponse(prettyPrint()),
*scope.snippets.toTypedArray()
)
)
}
mockMvc
.get("/search") {
param("q", keyword)
client()
}
.andExpect {
status { isOk }
content { contentType(MediaType.APPLICATION_JSON) }
}
.andDocument("/search/GET") {
request {
parameter("q")
.description("검색 키워드")
clientHeaders()
header("Referer")
.optional()
.description("리퍼러")
}
response {
field("items")
.type(JsonFieldType.ARRAY)
.description("아이템들.")
subsection("items[].title")
.type(JsonFieldType.STRING)
.description("제목.")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment