robfletcher / strikt

An assertion library for Kotlin
https://strikt.io/
Apache License 2.0
558 stars 61 forks source link

Want to add MockMvc DSL extension #293

Open christian-draeger opened 1 year ago

christian-draeger commented 1 year ago

I am using spring-boot projects a lot. All my tests using strikt as an assertions library already.

To test my rest controllers i mostly use mockMvc among my projects. but since assertions on mockMvc result body require to use hamcrest matchers it needs extra knowledge (aka learn a second assertion lib and its matchers). also the hamcrest matchers are not nice to use or type-safe.

since i know the expected result types of my controllers (which are part of my project anyway) i wrote a little extension to be able to deserialize the response body and use strikt matchers directly to assert body. i think this is a pretty common use-case.

i would like to add it upstream, but am unsure if it could be part of the strikt-spring extension or if it should be another (strikt-mockmvc) extension.

😢 what it looks like with vanilla mock mvc DSL (using hamcrest, not typesafe):

    @Test
    internal fun `example mockMvc test`() {
        mockMvc.get("http://localhost:$port/foo")
            .andExpect {
                status { isOk() }
                content {
                    jsonPath("$[0].title", Matchers.equalTo("foo"))
                    jsonPath("$.size()", Matchers.equalTo(2))
                }
            }
    }

🚸 what it looks like working around it to use strikt:

    @Test
    internal fun `example mockMvc test using strikt`() {
        mockMvc.get("http://localhost:$port/foo")
            .andExpect {
                status { isOk() }
            }.andDeserializeBody<List<ResultDto>>()

        expectThat(resultBody) {
            get { first().title }.isEqualTo("foo")
            hasSize(2)
        }
    }

    inline fun <reified T> ResultActionsDsl.andDeserializeBody(mapper: ObjectMapper = jacksonObjectMapper()): T =
    andReturn().response.contentAsString.let(mapper::readValue)

😎 what it looks like with fancy extension:

    @Test
    internal fun `example mockMvc test using strikt mockmvc extension`() {
        mockMvc.get("http://localhost:$port/foo")
            .andExpect {
                status { isOk() }
            }.andExpectBody<List<ResultDto>> {
                get { first().title }.isEqualTo("foo")
                hasSize(2)
            }
    }

    // what would basically be added to strikt as an extension to make the syntax above possible:
    inline fun <reified T> ResultActionsDsl.andDeserializeBody(mapper: ObjectMapper = jacksonObjectMapper()): T =
    andReturn().response.contentAsString.let(mapper::readValue)

    inline fun <reified T> ResultActionsDsl.andExpectBody(crossinline block: Assertion.Builder<T>.() -> Unit): ResultActionsDsl =
        also {
            andDeserializeBody<T>().run {
                expectThat(this) {
                    block()
                }
            }
        }