MockMvc Kotlin DSL

MockMvc Kotlin DSL

1. Visão geral

Neste tutorial rápido, daremos uma olhada no novo suporte específico para KotlinMockMvc disponível no Spring Framework 5.2+.

Nota: como a versão 5.2 ainda não é GA, precisamos usar SpringMilestone repository.

2. O controlador para testar

Vamos configurar o controlador que iremos testar.

Usaremos o seguinte domínio:

// Payload
data class Name(val first: String, val last: String)

// Web request
data class Request(val name: Name)

// Web response
@JsonInclude(JsonInclude.Include.NON_NULL) data class Response(val error: String?)

E um controlador REST que valida a carga útil que recebe:

@RestController
@RequestMapping("/mockmvc")
class MockMvcController {

    @RequestMapping(value = ["/validate"], method = [RequestMethod.POST],
      produces = [MediaType.APPLICATION_JSON_VALUE])
    fun validate(@RequestBody request: Request): Response {
        val error = if (request.name.first == "admin") {
            null
        } else {
            ERROR
        }
        return Response(error)
    }

    companion object {
        const val ERROR = "invalid user"
    }
}

Aqui, temos um endpoint POST que recebe uma instância de nossa classeRequest personalizada serializada para JSON e retorna uma instância de nossa classeResponse personalizada serializada para JSON.

3. Abordagem de teste clássico

Podemos testar o controlador acima usando a abordagem padrãoMockMvc:

mockMvc.perform(MockMvcRequestBuilders
  .post("/mockmvc/validate")
  .accept(MediaType.APPLICATION_JSON)
  .contentType(MediaType.APPLICATION_JSON)
  .content(mapper.writeValueAsString(Request(Name("admin", "")))))

  .andExpect(MockMvcResultMatchers.status().isOk)
  .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
  .andExpect(MockMvcResultMatchers.content().string("{}"))

No exemplo acima, usamos o suporte de teste padrão para verificar alguns aspectos:

  • o código de resposta HTTP é 200

  • o tipo de conteúdo da resposta éapplication/json

  • o conteúdo da resposta é um objeto JSON vazio

Na verdade, parece muito bom - limpo e expressivo. Maswe can make it even cleaner with Kotlin DSL.

4. Abordagem de teste moderna

O mesmo teste pode ser reescrito como:

mockMvc.post("/mockmvc/validate") {
  contentType = MediaType.APPLICATION_JSON
  content = mapper.writeValueAsString(Request(Name("admin", "")))
  accept = MediaType.APPLICATION_JSON
}.andExpect {
    status { isOk }
    content { contentType(MediaType.APPLICATION_JSON) }
    content { json("{}") }
}

Vamos verificar como isso é implementado. Em primeiro lugar, precisamos usar o Spring Framework 5.2+, que contémMockMvcExtensions.kt — a custom DSL for MockMvc.

Começamos chamando a função de extensãoMockMvc.post() com umMockHttpServletRequestDslextension method:

mockMvc.post("/mockmvc/validate") {
    // This is extension method's body
}

Isso significa que o método é executado com um objetoMockHttpServletRequestDsl como referênciathis, então acabamos de definir suas propriedadescontentType,content eaccept.

Em seguida, definimos as expectativas de maneira semelhante - fornecendo um método de extensãoMockMvcResultMatchersDsl:

andExpect {
    // Extension method body
}

5. Evolução adicional

Se quisermos adicionar mais testes, podemos refatorar o código existente para evitar duplicação.

Vamos extrair o código comum aqui em um métododoTest útil:

private fun doTest(input: Request, expectation: Response) {
    mockMvc.post("/mockmvc/validate") {
      contentType = MediaType.APPLICATION_JSON
      content = mapper.writeValueAsString(input)
      accept = MediaType.APPLICATION_JSON
    }.andExpect {
      status { isOk }
      content { contentType(MediaType.APPLICATION_JSON) }
      content { json(mapper.writeValueAsString(expectation)) }
    }
}

Agora, os testes reais serão simplificados:

@Test
fun `when supported user is given then validation is successful`() {
    doTest(Request(Name("admin", "")), Response(null))
}

@Test
fun `when unsupported user is given then validation is failed`() {
    doTest(Request(Name("some-name", "some-surname")), Response(MockMvcController.ERROR))
}

6. Conclusão

Neste artigo, verificamos comoMockMvc Kotlin DSL pode ser usado para tornar nosso código de teste mais limpo. Não é uma solução mágica, apenas um pequeno recurso que torna o código de teste ainda mais conciso.

Como este é mais um exemplo de uso de DSL personalizado, ele pode servir como motivação para começar a usar DSL personalizado em outros projetos também.

Como de costume, o código-fonte completo deste artigo está disponívelover on GitHub.