This is a test against a generic PUT endpoint. The .andExpect { status().isOk } assertion always passes, regardless of the actual result status, while .andExpect( status().isOk ) works as expected. I suspect this is a bug?
We're using spring-boot-starter-parent:2.6.6 on Kotlin 1.6.10 on a JDK 11.
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@SpringBootTest
@AutoConfigureMockMvc
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Test {
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun `should reject invalid payload`() {
mockMvc.perform( MockMvcRequestBuilders.put("/analyses/297e2c6c6a26a2ba016a27bd5f977f74")
.contentType(APPLICATION_JSON)
.content("invalid")
)
.andExpect { status().isOk } // does not work
.andExpect( status().isOk ) // works
}
}
Comment From: jnizet
I can see how this is confusing, but I don't really see it as a bug.
andExpect() expects a ResultMatcher.
In the working case, that's what you're passing: status().isOk is an expression of type ResultMatcher. When it's match() method is called with an MvcResult, it checks that the status in the MvcResult is OK. So the working code is equivalent to
.andExpect { (mvcResult: MvcResult) ->
status().isOk.match(mvcResult)
}
In the non-working case, you're passing { status().isOk }. This is a lambda which creates a ResultMatcher but never calls it. I.e. it's equivalent to
.andExpect { (mvcResult: MvcResult) ->
val matcher: ResultMatcher = status().isOk
return Unit
}
Note how the mvcResult is never used anywhere in this lambda, and how the ResultMatcher it creates is never used either.
An alternative would be to use the MockMvc Kotlin DSL:
mockMvc.put("/analyses/297e2c6c6a26a2ba016a27bd5f977f74") {
contentType = APPLICATION_JSON
content = "invalid"
}.andExpect { status { isOk() } }
Comment From: MrBuddyCasino
You're totally right, thanks for the detailed explanation! This is an example of the main failure mode of DSLs in my experience: what actually happens becomes more obfuscated in the quest for aesthetic call chains. Sorry to have wasted your time.
Comment From: kegt
So I think the official documents are being misleading here.
On https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#spring-mvc-test-server-defining-expectations you can see exactly the same code for kotlin.