spring-restdocs icon indicating copy to clipboard operation
spring-restdocs copied to clipboard

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

Open ghost opened this issue 4 years ago • 6 comments

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"))
}

ghost avatar Apr 02 '20 05:04 ghost

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.

wilkinsona avatar Apr 02 '20 07:04 wilkinsona

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

ghost avatar Apr 02 '20 22:04 ghost

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.

wilkinsona avatar Apr 06 '20 08:04 wilkinsona

@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"))
    }
}

countableSet avatar Feb 17 '21 22:02 countableSet

@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?

wilkinsona avatar Feb 18 '21 14:02 wilkinsona

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.

sdeleuze avatar Apr 02 '21 16:04 sdeleuze