spring-projects / spring-restdocs

Test-driven documentation for RESTful services
https://spring.io/projects/spring-restdocs
Apache License 2.0
1.16k stars 735 forks source link

Document how to use Spring REST Docs with the MockMVC Kotlin DSL #677

Open ghost opened 4 years ago

ghost commented 4 years ago

Was trying to implement RestDocs MockMvc in our Kotlin project However noticed that when using the MockMvc Kotlin DSL snippets were not generated e.g.

    fun shouldReturnDefaultMessageKotlinDSL() {
        mockMvc.get("/hello")
        .andExpect {
            status { isOk }
            content {
                string(containsString("Hello, world!"))
            }
        }.andDo {
            print()
            document("home")
        }
    }

However using the java format did correctly generate e.g.

fun shouldReturnDefaultMessage() {
this.mockMvc.perform(get("/hello"))
.andDo(print())
.andExpect(status().isOk)
.andExpect(content().string(containsString("Hello, world!")))
.andDo(document("home"))
}
wilkinsona commented 4 years ago

Thanks for the report. REST Docs doesn't contain any Kotlin-specific code so I suspect that this is a problem with the MockMvc Kotlin DSL rather than REST Docs. To help us to figure out if that's the case, can you please provide a complete and minimal sample that reproduces the problem? You can do so by zipping something up and attaching it to this issue or by pushing it to a separate repository.

ghost commented 4 years ago

Hi wilkinsona, have uploaded example to repo here: https://github.com/seanyyyyyy/spring-cloud-kotlin-producer/blob/master/src/test/kotlin/com/example/cloudkotlin/testingrestdocs/RestdocsHelloControllerTest.kt

wilkinsona commented 4 years ago

Thanks for the sample. When using the Kotlin DSL for MockMvc, you need to pass the result handler that is returned from document(…) to handle(ResultHandler):

@Test
fun shouldReturnDefaultMessageKotlinDSL() {
    mockMvc.get("/hello")
    .andExpect {
       status { isOk }
       content {
            string(containsString("Hello, World"))
        }
    }.andDo {
        print()
        handle(document("home"))
    }
}

It's a shame that for this one piece, the Kotlin DSL isn't quite as elegant as the Java equivalent. I'm not sure how it could be improved while keeping the existing more elegant access to the built-in result handlers such as print(). @sdeleuze any suggestions here?

I'll keep this issue open as I think it's worth documenting the above.

countableSet commented 3 years ago

@wilkinsona Looks like there might be another issue with url templates. I'm seeing the following exception when using url templates in the request:

urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?
java.lang.IllegalArgumentException: urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?
    at org.springframework.util.Assert.notNull(Assert.java:201)
    at org.springframework.restdocs.request.PathParametersSnippet.extractUrlTemplate(PathParametersSnippet.java:126)
    at org.springframework.restdocs.request.PathParametersSnippet.extractActualParameters(PathParametersSnippet.java:113)
    at org.springframework.restdocs.request.AbstractParametersSnippet.verifyParameterDescriptors(AbstractParametersSnippet.java:89)
    at org.springframework.restdocs.request.AbstractParametersSnippet.createModel(AbstractParametersSnippet.java:74)
    at org.springframework.restdocs.request.PathParametersSnippet.createModel(PathParametersSnippet.java:98)
    at org.springframework.restdocs.snippet.TemplatedSnippet.document(TemplatedSnippet.java:78)
    at org.springframework.restdocs.generate.RestDocumentationGenerator.handle(RestDocumentationGenerator.java:191)
    at org.springframework.restdocs.mockmvc.RestDocumentationResultHandler.handle(RestDocumentationResultHandler.java:52)
    at org.springframework.test.web.servlet.MockMvc$1.andDo(MockMvc.java:201)
    at org.springframework.test.web.servlet.MockMvcResultHandlersDsl.handle(MockMvcResultHandlersDsl.kt:63)
    at com.exmaple.docs.DocTest$getTests$5.invoke(DocTest.kt:10)

But adding requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/hello/{name}) fixes it, but not a great solution.

@Test
fun shouldReturnDefaultMessageKotlinDSL() {
    mockMvc.get("/hello/{name}", name) { 
        requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/hello/{name}")
    }.andExpect {
       status { isOk }
       content {
            string(containsString("Hello, World"))
        }
    }.andDo {
        print()
        handle(document("home"))
    }
}
wilkinsona commented 3 years ago

@countableSet Yeah, that's not great. Unfortunately, the Kotlin DSL hardcodes the use of MockMvcRequestBuilders so that may be the best that can be done at the moment.

@sdeleuze, do you have any recommendations here please?

sdeleuze commented 3 years ago

Sorry for the delay of my response.

It's a shame that for this one piece, the Kotlin DSL isn't quite as elegant as the Java equivalent. I'm not sure how it could be improved while keeping the existing more elegant access to the built-in result handlers such as print(). @sdeleuze any suggestions here?

You could probably extend MockMvc DSL in Spring REST Docs with something like:

fun MockMvcResultHandlersDsl.document(identifier: String, vararg snippets: Snippet) {
      handle(MockMvcRestDocumentation.document(identifier, *snippets))
  }

@countableSet Yeah, that's not great. Unfortunately, the Kotlin DSL hardcodes the use of MockMvcRequestBuilders so that may be the best that can be done at the moment.

@sdeleuze, do you have any recommendations here please?

Let's maybe discuss that in https://github.com/spring-projects/spring-restdocs/issues/547#issuecomment-812611417.