Skip to content

Add Kotlin DSL for working with MockMvc #1951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.test.web.servlet

import org.springframework.http.HttpMethod
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import java.net.URI

/**
*
* @author Clint Checketts
* @author Petr Balat
*/

/**
* Create a [MockMvcRequestDsl] with the given HTTP verb.
* @param method the HTTP method (GET, POST, etc)
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param uriVars zero or more URI variables
* @param block lambda for configuring [MockMvcRequestDsl]
* @return the [MvcResult] (never `null`)
* @since 5.1.0
*
*/
fun MockMvc.perform(method: HttpMethod, urlTemplate:String, vararg uriVars: String, block: MockMvcRequestDsl.() -> Unit = {}): MvcResult {
return performDsl(this, MockMvcRequestBuilders.request(method, urlTemplate, *uriVars), block)
}

/**
* Create a [MockMvcRequestDsl] with the given HTTP verb.
* @param method the HTTP method (GET, POST, etc)
* @param uri the URL
* @param block lambda for configuring [MockMvcRequestDsl]
* @return the [MvcResult] (never `null`)
* @since 5.1.0
*/
fun MockMvc.perform(method: HttpMethod, uri: URI, block: MockMvcRequestDsl.() -> Unit = {}): MvcResult {
return performDsl(this, MockMvcRequestBuilders.request(method, uri), block)
}

private fun performDsl(mockMvc: MockMvc,
requestBuilder: MockHttpServletRequestBuilder,
block: MockMvcRequestDsl.() -> Unit = {}): MvcResult {
val request = MockMvcRequestDsl(requestBuilder).apply(block)
val result = mockMvc.perform(request.buildRequest())
return request.applyResult(result).andReturn()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.test.web.servlet

import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
import org.springframework.test.web.servlet.result.*


/**
* **Main entry point for server-side Spring MVC test support.**
*
* ### Example
*
* ```
* import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
*
* // ...
*
* WebApplicationContext wac = ...;
*
* MockMvc mockMvc = webAppContextSetup(wac).build();
*
* mockMvc.performGet("/form") {
* expect {
* status { isOk }
* content { mimeType("text/html") }
* forwardedUrl("/WEB-INF/layouts/main.jsp")
* }
* }
* ```
*
* @constructor Creates an `MockMvcRequestDsl`.
* @author Clint Checketts
* @author Petr Balat
*
* Dsl for working with [MockMVC]
*
* @since 5.1.0
*/
class MockMvcRequestDsl(private val requestBuilder: MockHttpServletRequestBuilder) {

private val requestBuilders: MutableList<MockHttpServletRequestBuilder.() -> Unit> = mutableListOf()

private val actions: MutableList<ResultActions.() -> Unit> = mutableListOf()

/**
* Print {@link MvcResult} details to the "standard" output stream.
* @see [MockMvcResultHandlers.print]
*/
fun print() {
actions { andDo(MockMvcResultHandlers.print()) }
}

/**
* Configure the [MockHttpServletRequestBuilder]
* @param block receiver block for configuring [MockHttpServletRequestBuilder]
*/
fun builder(block: MockHttpServletRequestBuilder.() -> Unit) {
requestBuilders.add(block)
}

/**
* Configure the [ResultActionsExpectationsDsl]
* @param block receiver block for configuring request expectation via a [ResultActionsExpectationsDsl]
*/
fun expect(block: ResultActionsExpectationsDsl.() -> Unit) {
this.actions { ResultActionsExpectationsDsl(this).apply(block) }
}

/**
* Allows adding addition post-request actions if the provided [builder] or [expect] blocks aren't sufficient
* @param block receiver block for configuring additional [ResultActions]
*/
fun actions(block: ResultActions.() -> Unit) {
this.actions.add(block)
}

fun buildRequest(): RequestBuilder {
requestBuilders.forEach { requestBuilder.apply(it) }
return requestBuilder
}

fun applyResult(result: ResultActions): ResultActions {
actions.forEach { result.apply(it) }
return result
}

fun andDo(action: ResultHandler): MockMvcRequestDsl {
actions { andDo(action)}
return this
}

fun andExpect(action: ResultMatcher): MockMvcRequestDsl {
actions { andExpect(action)}
return this
}

fun withResult(block: MvcResult.() -> Unit) {
actions { andReturn().apply(block) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.test.web.servlet

import org.hamcrest.CoreMatchers
import org.hamcrest.Matcher
import org.springframework.http.HttpStatus
import org.springframework.test.web.servlet.result.*

/**
*
* @author Clint Checketts
* @author Petr Balat
*/
class ResultActionsExpectationsDsl(private val actions: ResultActions) {

fun status(statusInit: StatusResultMatchers.() -> ResultMatcher) {
val status = MockMvcResultMatchers.status().statusInit()
actions.andExpect(status)
}

fun content(contentInit: ContentResultMatchers.() -> ResultMatcher) {
val content = MockMvcResultMatchers.content().contentInit()
actions.andExpect(content)
}

fun viewName(viewName: String) {
val view = MockMvcResultMatchers.view().name(viewName)
actions.andExpect(view)
}

fun model(modelInit: ModelResultMatchers.() -> ResultMatcher) {
val model = MockMvcResultMatchers.model().modelInit()
actions.andExpect(model)
}

fun <T> model(name: String, modelInit: T.() -> Unit) {
actions.andExpect { mvcResult ->
val model = mvcResult.modelAndView?.model?.get(name) as T?
model?.modelInit() ?: throw AssertionError("Model attribute $name was not found")
}
}

fun redirectedUrl(expectedUrl: String) {
val header = MockMvcResultMatchers.redirectedUrl(expectedUrl)
actions.andExpect(header)
}

fun redirectedUrlPattern(redirectedUrlPattern: String) {
val header = MockMvcResultMatchers.redirectedUrl(redirectedUrlPattern)
actions.andExpect(header)
}

fun header(headerInit: HeaderResultMatchers.() -> ResultMatcher) {
val header = MockMvcResultMatchers.header().headerInit()
actions.andExpect(header)
}

fun flash(flashInit: FlashAttributeResultMatchers.() -> ResultMatcher) {
val flash = MockMvcResultMatchers.flash().flashInit()
actions.andExpect(flash)
}

fun <T> jsonPath(expression:String, matcher : Matcher<T>) {
val json = MockMvcResultMatchers.jsonPath(expression, matcher)
actions.andExpect(json)
}

fun jsonPath(expression:String, vararg args:Any, block: JsonPathResultMatchers.() -> ResultMatcher) {
val xpath = MockMvcResultMatchers.jsonPath(expression, args).block()
actions.andExpect(xpath)
}

fun xpath(expression:String, vararg args:Any, xpathInit: XpathResultMatchers.() -> ResultMatcher) {
val xpath = MockMvcResultMatchers.xpath(expression, args).xpathInit()
actions.andExpect(xpath)
}

fun cookie(cookieInit: CookieResultMatchers.() -> ResultMatcher) {
val cookie = MockMvcResultMatchers.cookie().cookieInit()
actions.andExpect(cookie)
}

fun HttpStatus.isStatus() {
status { `is`([email protected]()) }
}

fun contentString(value: String){
content { string(value) }
}

infix fun String.jsonPath(block: JsonPathResultMatchers.() -> ResultMatcher) {
actions.andExpect(MockMvcResultMatchers.jsonPath(this).block())
}

infix fun String.jsonPathIs(value: Any?) {
actions.andExpect(MockMvcResultMatchers.jsonPath(this, CoreMatchers.`is`(value)))
}

infix fun <T> String.jsonPathMatcher(value: Matcher<T>) {
actions.andExpect(MockMvcResultMatchers.jsonPath(this, value))
}

operator fun ResultMatcher.unaryPlus(){
actions.andExpect(this)
}

fun json(jsonContent: String, strict: Boolean = false) {
actions.andExpect(MockMvcResultMatchers.content().json(jsonContent, strict))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.test.web.servlet

import org.springframework.test.web.servlet.result.*

/**
*
* @author Clint Checketts
* @author Petr Balat
*/
fun ResultActions.expectStatus(statusInit: StatusResultMatchers.() -> ResultMatcher) {
val status = MockMvcResultMatchers.status().statusInit()
this.andExpect(status)
}

fun ResultActions.expectContent(contentInit: ContentResultMatchers.() -> ResultMatcher) {
val content = MockMvcResultMatchers.content().contentInit()
this.andExpect(content)
}

fun ResultActions.expectViewName(viewName: String): ResultActions {
val view = MockMvcResultMatchers.view()
return this.andExpect(view.name(viewName))
}

fun ResultActions.expectModel(modelInit: ModelResultMatchers.() -> ResultMatcher): ResultActions {
val model = MockMvcResultMatchers.model().modelInit()
return this.andExpect(model)
}

fun <T> ResultActions.expectModel(name: String, modelInit: T.() -> Unit): ResultActions {
return this.andExpect { mvcResult ->
val model = mvcResult.modelAndView?.model?.get(name) as T?
model?.modelInit()
}
}

fun ResultActions.expectRedirectedUrl(expectedUrl: String): ResultActions {
val header = MockMvcResultMatchers.redirectedUrl(expectedUrl)
return this.andExpect(header)
}

fun ResultActions.expectRedirectedUrlPattern(redirectedUrlPattern: String): ResultActions {
val header = MockMvcResultMatchers.redirectedUrl(redirectedUrlPattern)
return this.andExpect(header)
}

fun ResultActions.expectHeader(headerInit: HeaderResultMatchers.() -> ResultMatcher): ResultActions {
val header = MockMvcResultMatchers.header().headerInit()
return this.andExpect(header)
}

fun ResultActions.expectFlash(flashInit: FlashAttributeResultMatchers.() -> ResultMatcher): ResultActions {
val flash = MockMvcResultMatchers.flash().flashInit()
return this.andExpect(flash)
}

fun ResultActions.expectJsonPath(expression: String, vararg args: Any, jsonInit: JsonPathResultMatchers.() -> ResultMatcher): ResultActions {
val json = MockMvcResultMatchers.jsonPath(expression, args).jsonInit()
return this.andExpect(json)
}

fun ResultActions.expectXPath(expression: String, vararg args: Any, xpatInit: XpathResultMatchers.() -> ResultMatcher): ResultActions {
val xpath = MockMvcResultMatchers.xpath(expression, args).xpatInit()
return this.andExpect(xpath)
}

fun ResultActions.expectCookie(cookieInit: CookieResultMatchers.() -> ResultMatcher): ResultActions {
val cookie = MockMvcResultMatchers.cookie().cookieInit()
return this.andExpect(cookie)
}
Loading