From d04c5f8b2cb274b8f44c47b3783e5d29f3e21b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Mon, 26 Oct 2020 18:13:39 +0100 Subject: [PATCH] Support multiple matchers in MockMvc Kotlin DSL Previous incarnation of MockMvc Kotlin DSL tried to reuse directly Java APIs like ModelResultMatchers or StatusResultMatchers, but when using multiple matchers in DSL blocks like model { } or status { }, only the last statement was taken in account which was very confusing. This refactoring provides dedicated Kotlin DSLs for matchers. The main API breaking changes is that functions like isOk() need to be invoked with the parenthesis, isOk is not supported anymore (on purpose). Closes gh-24103 --- .../result/FlashAttributeResultMatchers.java | 3 +- .../result/JsonPathResultMatchers.java | 2 +- .../servlet/result/ModelResultMatchers.java | 3 +- .../servlet/result/RequestResultMatchers.java | 7 +- .../web/servlet/MockMvcResultMatchersDsl.kt | 41 +- .../result/ContentResultMatchersDsl.kt | 118 ++++ .../servlet/result/CookieResultMatchersDsl.kt | 143 +++++ .../result/FlashAttributeResultMatchersDsl.kt | 59 ++ .../servlet/result/HeaderResultMatchersDsl.kt | 86 +++ .../result/JsonPathResultMatchersDsl.kt | 130 +++++ .../servlet/result/ModelResultMatchersDsl.kt | 129 +++++ .../result/RequestResultMatchersDsl.kt | 94 ++++ .../servlet/result/StatusResultMatchersDsl.kt | 515 ++++++++++++++++++ .../servlet/result/ViewResultMatchersDsl.kt | 46 ++ .../servlet/result/XpathResultMatchersDsl.kt | 110 ++++ .../web/servlet/MockMvcExtensionsTests.kt | 32 +- 16 files changed, 1483 insertions(+), 35 deletions(-) create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ContentResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/CookieResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/FlashAttributeResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/HeaderResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/JsonPathResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ModelResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/RequestResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ViewResultMatchersDsl.kt create mode 100644 spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/XpathResultMatchersDsl.kt diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/FlashAttributeResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/FlashAttributeResultMatchers.java index b403f1d1b1..ce66b04499 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/FlashAttributeResultMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/FlashAttributeResultMatchers.java @@ -18,6 +18,7 @@ package org.springframework.test.web.servlet.result; import org.hamcrest.Matcher; +import org.springframework.lang.Nullable; import org.springframework.test.web.servlet.ResultMatcher; import static org.hamcrest.MatcherAssert.assertThat; @@ -54,7 +55,7 @@ public class FlashAttributeResultMatchers { /** * Assert a flash attribute's value. */ - public ResultMatcher attribute(String name, Object value) { + public ResultMatcher attribute(String name, @Nullable Object value) { return result -> assertEquals("Flash attribute '" + name + "'", value, result.getFlashMap().get(name)); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java index f2f78967bc..a0d882e2d5 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java @@ -107,7 +107,7 @@ public class JsonPathResultMatchers { * @see #value(Matcher) * @see #value(Matcher, Class) */ - public ResultMatcher value(Object expectedValue) { + public ResultMatcher value(@Nullable Object expectedValue) { return result -> this.jsonPathHelper.assertValue(getContent(result), expectedValue); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/ModelResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/ModelResultMatchers.java index 00bf0626fe..6cb0fce1f8 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/ModelResultMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/ModelResultMatchers.java @@ -18,6 +18,7 @@ package org.springframework.test.web.servlet.result; import org.hamcrest.Matcher; +import org.springframework.lang.Nullable; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.ui.ModelMap; @@ -67,7 +68,7 @@ public class ModelResultMatchers { /** * Assert a model attribute value. */ - public ResultMatcher attribute(String name, Object value) { + public ResultMatcher attribute(String name, @Nullable Object value) { return result -> { ModelAndView mav = getModelAndView(result); assertEquals("Model attribute '" + name + "'", value, mav.getModel().get(name)); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/RequestResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/RequestResultMatchers.java index eb1c63bf5d..926e3f4349 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/RequestResultMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/RequestResultMatchers.java @@ -23,6 +23,7 @@ import javax.servlet.http.HttpSession; import org.hamcrest.Matcher; +import org.springframework.lang.Nullable; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.util.Assert; @@ -98,7 +99,7 @@ public class RequestResultMatchers { * or {@link WebAsyncTask}. The value matched is the value returned from the * {@code Callable} or the exception raised. */ - public ResultMatcher asyncResult(Object expectedResult) { + public ResultMatcher asyncResult(@Nullable Object expectedResult) { return result -> { HttpServletRequest request = result.getRequest(); assertAsyncStarted(request); @@ -120,7 +121,7 @@ public class RequestResultMatchers { /** * Assert a request attribute value. */ - public ResultMatcher attribute(String name, Object expectedValue) { + public ResultMatcher attribute(String name, @Nullable Object expectedValue) { return result -> assertEquals("Request attribute '" + name + "'", expectedValue, result.getRequest().getAttribute(name)); } @@ -141,7 +142,7 @@ public class RequestResultMatchers { /** * Assert a session attribute value. */ - public ResultMatcher sessionAttribute(String name, Object value) { + public ResultMatcher sessionAttribute(String name, @Nullable Object value) { return result -> { HttpSession session = result.getRequest().getSession(); Assert.state(session != null, "No HttpSession"); diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/MockMvcResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/MockMvcResultMatchersDsl.kt index 6a5d909025..8971a80f4b 100644 --- a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/MockMvcResultMatchersDsl.kt +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/MockMvcResultMatchersDsl.kt @@ -30,29 +30,29 @@ class MockMvcResultMatchersDsl internal constructor (private val actions: Result /** * @see MockMvcResultMatchers.request */ - fun request(matcher: RequestResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.request().matcher()) + fun request(dsl: RequestResultMatchersDsl.() -> Unit) { + RequestResultMatchersDsl(actions).dsl() } /** * @see MockMvcResultMatchers.view */ - fun view(matcher: ViewResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.view().matcher()) + fun view(dsl: ViewResultMatchersDsl.() -> Unit) { + ViewResultMatchersDsl(actions).dsl() } /** * @see MockMvcResultMatchers.model */ - fun model(matcher: ModelResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.model().matcher()) + fun model(dsl: ModelResultMatchersDsl.() -> Unit) { + ModelResultMatchersDsl(actions).dsl() } /** * @see MockMvcResultMatchers.flash */ - fun flash(matcher: FlashAttributeResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.flash().matcher()) + fun flash(dsl: FlashAttributeResultMatchersDsl.() -> Unit) { + FlashAttributeResultMatchersDsl(actions).dsl() } /** @@ -93,22 +93,22 @@ class MockMvcResultMatchersDsl internal constructor (private val actions: Result /** * @see MockMvcResultMatchers.status */ - fun status(matcher: StatusResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.status().matcher()) + fun status(dsl: StatusResultMatchersDsl.() -> Unit) { + StatusResultMatchersDsl(actions).dsl() } /** * @see MockMvcResultMatchers.header */ - fun header(matcher: HeaderResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.header().matcher()) + fun header(dsl: HeaderResultMatchersDsl.() -> Unit) { + HeaderResultMatchersDsl(actions).dsl() } /** * @see MockMvcResultMatchers.content */ - fun content(matcher: ContentResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.content().matcher()) + fun content(dsl: ContentResultMatchersDsl.() -> Unit) { + ContentResultMatchersDsl(actions).dsl() } /** @@ -121,23 +121,22 @@ class MockMvcResultMatchersDsl internal constructor (private val actions: Result /** * @see MockMvcResultMatchers.jsonPath */ - fun jsonPath(expression: String, vararg args: Any, block: JsonPathResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.jsonPath(expression, *args).block()) + fun jsonPath(expression: String, vararg args: Any?, dsl: JsonPathResultMatchersDsl.() -> Unit) { + JsonPathResultMatchersDsl(actions, expression, *args).dsl() } /** * @see MockMvcResultMatchers.xpath */ - fun xpath(expression: String, vararg args: Any, namespaces: Map? = null, xpathInit: XpathResultMatchers.() -> ResultMatcher) { - actions.andExpect(MockMvcResultMatchers.xpath(expression, namespaces, args).xpathInit()) + fun xpath(expression: String, vararg args: Any?, namespaces: Map? = null, dsl: XpathResultMatchersDsl.() -> Unit) { + XpathResultMatchersDsl(actions, expression, namespaces, *args).dsl() } /** * @see MockMvcResultMatchers.cookie */ - fun cookie(cookieInit: CookieResultMatchers.() -> ResultMatcher) { - val cookie = MockMvcResultMatchers.cookie().cookieInit() - actions.andExpect(cookie) + fun cookie(dsl: CookieResultMatchersDsl.() -> Unit) { + CookieResultMatchersDsl(actions).dsl() } /** diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ContentResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ContentResultMatchersDsl.kt new file mode 100644 index 0000000000..2ef41fdf1a --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ContentResultMatchersDsl.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.http.MediaType +import org.springframework.test.web.servlet.ResultActions +import org.w3c.dom.Node +import javax.xml.transform.Source + +/** + * Provide a [ContentResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class ContentResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.content() + + /** + * @see ContentResultMatchers.contentType + */ + fun contentType(contentType: String) { + actions.andExpect(matchers.contentType(contentType)) + } + + /** + * @see ContentResultMatchers.contentType + */ + fun contentType(contentType: MediaType) { + actions.andExpect(matchers.contentType(contentType)) + } + + /** + * @see ContentResultMatchers.contentTypeCompatibleWith + */ + fun contentTypeCompatibleWith(contentType: String) { + actions.andExpect(matchers.contentTypeCompatibleWith(contentType)) + } + + /** + * @see ContentResultMatchers.contentTypeCompatibleWith + */ + fun contentTypeCompatibleWith(contentType: MediaType) { + actions.andExpect(matchers.contentTypeCompatibleWith(contentType)) + } + + /** + * @see ContentResultMatchers.encoding + */ + fun encoding(contentType: String) { + actions.andExpect(matchers.encoding(contentType)) + } + + /** + * @see ContentResultMatchers.string + */ + fun string(matcher: Matcher) { + actions.andExpect(matchers.string(matcher)) + } + + /** + * @see ContentResultMatchers.string + */ + fun string(expectedContent: String) { + actions.andExpect(matchers.string(expectedContent)) + } + + /** + * @see ContentResultMatchers.bytes + */ + fun bytes(expectedContent: ByteArray) { + actions.andExpect(matchers.bytes(expectedContent)) + } + + /** + * @see ContentResultMatchers.xml + */ + fun xml(xmlContent: String) { + actions.andExpect(matchers.xml(xmlContent)) + } + + /** + * @see ContentResultMatchers.node + */ + fun node(matcher: Matcher) { + actions.andExpect(matchers.node(matcher)) + } + + /** + * @see ContentResultMatchers.source + */ + fun source(matcher: Matcher) { + actions.andExpect(matchers.source(matcher)) + } + + /** + * @see ContentResultMatchers.json + */ + fun json(jsonContent: String, strict: Boolean = false) { + actions.andExpect(matchers.json(jsonContent, strict)) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/CookieResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/CookieResultMatchersDsl.kt new file mode 100644 index 0000000000..987f7e19e3 --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/CookieResultMatchersDsl.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [CookieResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class CookieResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.cookie() + + /** + * @see CookieResultMatchers.value + */ + fun value(name: String, matcher: Matcher) { + actions.andExpect(matchers.value(name, matcher)) + } + + /** + * @see CookieResultMatchers.value + */ + fun value(name: String, expectedValue: String) { + actions.andExpect(matchers.value(name, expectedValue)) + } + + /** + * @see CookieResultMatchers.exists + */ + fun exists(name: String) { + actions.andExpect(matchers.exists(name)) + } + + /** + * @see CookieResultMatchers.doesNotExist + */ + fun doesNotExist(name: String) { + actions.andExpect(matchers.doesNotExist(name)) + } + + /** + * @see CookieResultMatchers.maxAge + */ + fun maxAge(name: String, matcher: Matcher) { + actions.andExpect(matchers.maxAge(name, matcher)) + } + + /** + * @see CookieResultMatchers.maxAge + */ + fun maxAge(name: String, maxAge: Int) { + actions.andExpect(matchers.maxAge(name, maxAge)) + } + + /** + * @see CookieResultMatchers.path + */ + fun path(name: String, matcher: Matcher) { + actions.andExpect(matchers.path(name, matcher)) + } + + /** + * @see CookieResultMatchers.path + */ + fun path(name: String, path: String) { + actions.andExpect(matchers.path(name, path)) + } + + /** + * @see CookieResultMatchers.domain + */ + fun domain(name: String, matcher: Matcher) { + actions.andExpect(matchers.domain(name, matcher)) + } + + /** + * @see CookieResultMatchers.domain + */ + fun domain(name: String, domain: String) { + actions.andExpect(matchers.domain(name, domain)) + } + + /** + * @see CookieResultMatchers.comment + */ + fun comment(name: String, matcher: Matcher) { + actions.andExpect(matchers.comment(name, matcher)) + } + + /** + * @see CookieResultMatchers.comment + */ + fun comment(name: String, comment: String) { + actions.andExpect(matchers.comment(name, comment)) + } + + /** + * @see CookieResultMatchers.version + */ + fun version(name: String, matcher: Matcher) { + actions.andExpect(matchers.version(name, matcher)) + } + + /** + * @see CookieResultMatchers.version + */ + fun version(name: String, version: Int) { + actions.andExpect(matchers.version(name, version)) + } + + /** + * @see CookieResultMatchers.secure + */ + fun secure(name: String, secure: Boolean) { + actions.andExpect(matchers.secure(name, secure)) + } + + /** + * @see CookieResultMatchers.httpOnly + */ + fun httpOnly(name: String, httpOnly: Boolean) { + actions.andExpect(matchers.httpOnly(name, httpOnly)) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/FlashAttributeResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/FlashAttributeResultMatchersDsl.kt new file mode 100644 index 0000000000..13175b282f --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/FlashAttributeResultMatchersDsl.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [FlashAttributeResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class FlashAttributeResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.flash() + + /** + * @see FlashAttributeResultMatchers.attribute + */ + fun attribute(name: String, matcher: Matcher) { + actions.andExpect(matchers.attribute(name, matcher)) + } + + /** + * @see FlashAttributeResultMatchers.attribute + */ + fun attribute(name: String, value: Any?) { + actions.andExpect(matchers.attribute(name, value)) + } + + /** + * @see FlashAttributeResultMatchers.attributeExists + */ + fun attributeExists(vararg name: String) { + actions.andExpect(matchers.attributeExists(*name)) + } + + /** + * @see FlashAttributeResultMatchers.attributeCount + */ + fun attributeCount(count: Int) { + actions.andExpect(matchers.attributeCount(count)) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/HeaderResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/HeaderResultMatchersDsl.kt new file mode 100644 index 0000000000..a8afcb4da7 --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/HeaderResultMatchersDsl.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [HeaderResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class HeaderResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.header() + + /** + * @see HeaderResultMatchersDsl.string + */ + fun string(name: String, matcher: Matcher) { + actions.andExpect(matchers.string(name, matcher)) + } + + /** + * @see HeaderResultMatchersDsl.stringValues + */ + fun stringValues(name: String, matcher: Matcher>) { + actions.andExpect(matchers.stringValues(name, matcher)) + } + + /** + * @see HeaderResultMatchersDsl.string + */ + fun string(name: String, value: String) { + actions.andExpect(matchers.string(name, value)) + } + + /** + * @see HeaderResultMatchersDsl.stringValues + */ + fun stringValues(name: String, vararg value: String) { + actions.andExpect(matchers.stringValues(name, *value)) + } + + /** + * @see HeaderResultMatchersDsl.exists + */ + fun exists(name: String) { + actions.andExpect(matchers.exists(name)) + } + + /** + * @see HeaderResultMatchersDsl.doesNotExist + */ + fun doesNotExist(name: String) { + actions.andExpect(matchers.doesNotExist(name)) + } + + /** + * @see HeaderResultMatchersDsl.longValue + */ + fun longValue(name: String, value: Long) { + actions.andExpect(matchers.longValue(name, value)) + } + /** + * @see HeaderResultMatchersDsl.dateValue + */ + fun dateValue(name: String, value: Long) { + actions.andExpect(matchers.dateValue(name, value)) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/JsonPathResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/JsonPathResultMatchersDsl.kt new file mode 100644 index 0000000000..0993903b3b --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/JsonPathResultMatchersDsl.kt @@ -0,0 +1,130 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [JsonPathResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +@Suppress("UsePropertyAccessSyntax") +class JsonPathResultMatchersDsl internal constructor(@PublishedApi internal val actions: ResultActions, expression: String, vararg args: Any?) { + + @PublishedApi + internal val matchers = MockMvcResultMatchers.jsonPath(expression, args) + + /** + * @see JsonPathResultMatchers.prefix + */ + fun prefix(prefix: String) { + matchers.prefix(prefix) + } + + /** + * @see JsonPathResultMatchers.value + */ + inline fun value(matcher: Matcher) { + actions.andExpect(matchers.value(matcher, T::class.java)) + } + + /** + * @see JsonPathResultMatchers.value + */ + fun value(expectedValue: Any?) { + actions.andExpect(matchers.value(expectedValue)) + } + + /** + * @see JsonPathResultMatchers.exists + */ + fun exists() { + actions.andExpect(matchers.exists()) + } + + /** + * @see JsonPathResultMatchers.doesNotExist + */ + fun doesNotExist() { + actions.andExpect(matchers.doesNotExist()) + } + + /** + * @see JsonPathResultMatchers.isEmpty + */ + fun isEmpty() { + actions.andExpect(matchers.isEmpty()) + } + + /** + * @see JsonPathResultMatchers.isNotEmpty + */ + fun isNotEmpty() { + actions.andExpect(matchers.isNotEmpty()) + } + + /** + * @see JsonPathResultMatchers.hasJsonPath + */ + fun hasJsonPath() { + actions.andExpect(matchers.hasJsonPath()) + } + + /** + * @see JsonPathResultMatchers.doesNotHaveJsonPath + */ + fun doesNotHaveJsonPath() { + actions.andExpect(matchers.doesNotHaveJsonPath()) + } + + /** + * @see JsonPathResultMatchers.isString + */ + fun isString() { + actions.andExpect(matchers.isString()) + } + + /** + * @see JsonPathResultMatchers.isBoolean + */ + fun isBoolean() { + actions.andExpect(matchers.isBoolean()) + } + + /** + * @see JsonPathResultMatchers.isNumber + */ + fun isNumber() { + actions.andExpect(matchers.isNumber()) + } + + /** + * @see JsonPathResultMatchers.isArray + */ + fun isArray() { + actions.andExpect(matchers.isArray()) + } + + /** + * @see JsonPathResultMatchers.isMap + */ + fun isMap() { + actions.andExpect(matchers.isMap()) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ModelResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ModelResultMatchersDsl.kt new file mode 100644 index 0000000000..68dc88465a --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ModelResultMatchersDsl.kt @@ -0,0 +1,129 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [ModelResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class ModelResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.model() + + /** + * @see ModelResultMatchers.attribute + */ + fun attribute(name: String, matcher: Matcher) { + actions.andExpect(matchers.attribute(name, matcher)) + } + + /** + * @see ModelResultMatchers.attribute + */ + fun attribute(name: String, value: Any?) { + actions.andExpect(matchers.attribute(name, value)) + } + + /** + * @see ModelResultMatchers.attributeExists + */ + fun attributeExists(vararg name: String) { + actions.andExpect(matchers.attributeExists(*name)) + } + + /** + * @see ModelResultMatchers.attributeDoesNotExist + */ + fun attributeDoesNotExist(vararg name: String) { + actions.andExpect(matchers.attributeDoesNotExist(*name)) + } + + /** + * @see ModelResultMatchers.attributeErrorCount + */ + fun attributeErrorCount(name: String, expectedCount: Int) { + actions.andExpect(matchers.attributeErrorCount(name, expectedCount)) + } + + /** + * @see ModelResultMatchers.attributeHasErrors + */ + fun attributeHasErrors(vararg name: String) { + actions.andExpect(matchers.attributeHasErrors(*name)) + } + + /** + * @see ModelResultMatchers.attributeHasNoErrors + */ + fun attributeHasNoErrors(vararg name: String) { + actions.andExpect(matchers.attributeHasNoErrors(*name)) + } + + /** + * @see ModelResultMatchers.attributeHasFieldErrors + */ + fun attributeHasFieldErrors(name: String, vararg fieldNames: String) { + actions.andExpect(matchers.attributeHasFieldErrors(name, *fieldNames)) + } + + /** + * @see ModelResultMatchers.attributeHasFieldErrorCode + */ + fun attributeHasFieldErrorCode(name: String, fieldName: String, code: String) { + actions.andExpect(matchers.attributeHasFieldErrorCode(name, fieldName, code)) + } + + /** + * @see ModelResultMatchers.attributeHasFieldErrorCode + */ + fun attributeHasFieldErrorCode(name: String, fieldName: String, matcher: Matcher) { + actions.andExpect(matchers.attributeHasFieldErrorCode(name, fieldName, matcher)) + } + + /** + * @see ModelResultMatchers.errorCount + */ + fun errorCount(expectedCount: Int) { + actions.andExpect(matchers.errorCount(expectedCount)) + } + + /** + * @see ModelResultMatchers.hasErrors + */ + fun hasErrors() { + actions.andExpect(matchers.hasErrors()) + } + + /** + * @see ModelResultMatchers.hasNoErrors + */ + fun hasNoErrors() { + actions.andExpect(matchers.hasNoErrors()) + } + + /** + * @see ModelResultMatchers.size + */ + fun size(size: Int) { + actions.andExpect(matchers.size(size)) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/RequestResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/RequestResultMatchersDsl.kt new file mode 100644 index 0000000000..9f82d6fc1a --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/RequestResultMatchersDsl.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [RequestResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class RequestResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.request() + + /** + * @see RequestResultMatchers.asyncStarted + */ + fun asyncStarted() { + actions.andExpect(matchers.asyncStarted()) + } + + /** + * @see RequestResultMatchers.asyncStarted + */ + fun asyncNotStarted() { + actions.andExpect(matchers.asyncNotStarted()) + } + + /** + * @see RequestResultMatchers.asyncResult + */ + fun asyncResult(matcher: Matcher) { + actions.andExpect(matchers.asyncResult(matcher)) + } + + /** + * @see RequestResultMatchers.asyncResult + */ + fun asyncResult(expectedResult: Any?) { + actions.andExpect(matchers.asyncResult(expectedResult)) + } + + /** + * @see RequestResultMatchers.attribute + */ + fun attribute(name: String, matcher: Matcher) { + actions.andExpect(matchers.attribute(name, matcher)) + } + + /** + * @see RequestResultMatchers.attribute + */ + fun attribute(name: String, expectedValue: Any?) { + actions.andExpect(matchers.attribute(name, expectedValue)) + } + + /** + * @see RequestResultMatchers.sessionAttribute + */ + fun sessionAttribute(name: String, matcher: Matcher) { + actions.andExpect(matchers.sessionAttribute(name, matcher)) + } + + /** + * @see RequestResultMatchers.sessionAttribute + */ + fun sessionAttribute(name: String, expectedValue: Any?) { + actions.andExpect(matchers.sessionAttribute(name, expectedValue)) + } + + /** + * @see RequestResultMatchers.sessionAttributeDoesNotExist + */ + fun sessionAttributeDoesNotExist(vararg names: String) { + actions.andExpect(matchers.sessionAttributeDoesNotExist(*names)) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt new file mode 100644 index 0000000000..ef5b774829 --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/StatusResultMatchersDsl.kt @@ -0,0 +1,515 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [StatusResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +@Suppress("UsePropertyAccessSyntax") +class StatusResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.status() + + /** + * @see StatusResultMatchers.is + */ + fun isEqualTo(matcher: Matcher) { + actions.andExpect(matchers.`is`(matcher)) + } + + /** + * @see StatusResultMatchers.is + */ + fun isEqualTo(status: Int) { + actions.andExpect(matchers.`is`(status)) + } + + /** + * @see StatusResultMatchers.is1xxInformational + */ + fun is1xxInformational() { + actions.andExpect(matchers.is1xxInformational()) + } + + /** + * @see StatusResultMatchers.is2xxSuccessful + */ + fun is2xxSuccessful() { + actions.andExpect(matchers.is2xxSuccessful()) + } + + /** + * @see StatusResultMatchers.is3xxRedirection + */ + fun is3xxRedirection() { + actions.andExpect(matchers.is3xxRedirection()) + } + + /** + * @see StatusResultMatchers.is4xxClientError + */ + fun is4xxClientError() { + actions.andExpect(matchers.is4xxClientError()) + } + + /** + * @see StatusResultMatchers.is5xxServerError + */ + fun is5xxServerError() { + actions.andExpect(matchers.is5xxServerError()) + } + + /** + * @see StatusResultMatchers.reason + */ + fun reason(matcher: Matcher) { + actions.andExpect(matchers.reason(matcher)) + } + + /** + * @see StatusResultMatchers.reason + */ + fun reason(reason: String) { + actions.andExpect(matchers.reason(reason)) + } + + /** + * @see StatusResultMatchers.isContinue + */ + fun isContinue() { + actions.andExpect(matchers.isContinue()) + } + + /** + * @see StatusResultMatchers.isSwitchingProtocols + */ + fun isSwitchingProtocols() { + actions.andExpect(matchers.isSwitchingProtocols()) + } + + /** + * @see StatusResultMatchers.isProcessing + */ + fun isProcessing() { + actions.andExpect(matchers.isProcessing()) + } + + /** + * @see StatusResultMatchers.isCheckpoint + */ + fun isCheckpoint() { + actions.andExpect(matchers.isCheckpoint()) + } + + /** + * @see StatusResultMatchers.isOk + */ + fun isOk() { + actions.andExpect(matchers.isOk()) + } + + /** + * @see StatusResultMatchers.isCreated + */ + fun isCreated() { + actions.andExpect(matchers.isCreated()) + } + + /** + * @see StatusResultMatchers.isAccepted + */ + fun isAccepted() { + actions.andExpect(matchers.isAccepted()) + } + + /** + * @see StatusResultMatchers.isNonAuthoritativeInformation + */ + fun isNonAuthoritativeInformation() { + actions.andExpect(matchers.isNonAuthoritativeInformation()) + } + + /** + * @see StatusResultMatchers.isNoContent + */ + fun isNoContent() { + actions.andExpect(matchers.isNoContent()) + } + + /** + * @see StatusResultMatchers.isResetContent + */ + fun isResetContent() { + actions.andExpect(matchers.isResetContent()) + } + + /** + * @see StatusResultMatchers.isPartialContent + */ + fun isPartialContent() { + actions.andExpect(matchers.isPartialContent()) + } + + /** + * @see StatusResultMatchers.isMultiStatus + */ + fun isMultiStatus() { + actions.andExpect(matchers.isMultiStatus()) + } + + /** + * @see StatusResultMatchers.isAlreadyReported + */ + fun isAlreadyReported() { + actions.andExpect(matchers.isAlreadyReported()) + } + + /** + * @see StatusResultMatchers.isImUsed + */ + fun isImUsed() { + actions.andExpect(matchers.isImUsed()) + } + + /** + * @see StatusResultMatchers.isMultipleChoices + */ + fun isMultipleChoices() { + actions.andExpect(matchers.isMultipleChoices()) + } + + /** + * @see StatusResultMatchers.isFound + */ + fun isFound() { + actions.andExpect(matchers.isFound()) + } + + /** + * @see StatusResultMatchers.isSeeOther + */ + fun isSeeOther() { + actions.andExpect(matchers.isSeeOther()) + } + + /** + * @see StatusResultMatchers.isNotModified + */ + fun isNotModified() { + actions.andExpect(matchers.isNotModified()) + } + + /** + * @see StatusResultMatchers.isTemporaryRedirect + */ + fun isTemporaryRedirect() { + actions.andExpect(matchers.isTemporaryRedirect()) + } + + /** + * @see StatusResultMatchers.isPermanentRedirect + */ + fun isPermanentRedirect() { + actions.andExpect(matchers.isPermanentRedirect()) + } + + /** + * @see StatusResultMatchers.isBadRequest + */ + fun isBadRequest() { + actions.andExpect(matchers.isBadRequest()) + } + + /** + * @see StatusResultMatchers.isUnauthorized + */ + fun isUnauthorized() { + actions.andExpect(matchers.isUnauthorized()) + } + + /** + * @see StatusResultMatchers.isPaymentRequired + */ + fun isPaymentRequired() { + actions.andExpect(matchers.isPaymentRequired()) + } + + /** + * @see StatusResultMatchers.isForbidden + */ + fun isForbidden() { + actions.andExpect(matchers.isForbidden()) + } + + /** + * @see StatusResultMatchers.isNotFound + */ + fun isNotFound() { + actions.andExpect(matchers.isNotFound()) + } + + /** + * @see StatusResultMatchers.isMethodNotAllowed + */ + fun isMethodNotAllowed() { + actions.andExpect(matchers.isMethodNotAllowed()) + } + + /** + * @see StatusResultMatchers.isNotAcceptable + */ + fun isNotAcceptable() { + actions.andExpect(matchers.isNotAcceptable()) + } + + /** + * @see StatusResultMatchers.isProxyAuthenticationRequired + */ + fun isProxyAuthenticationRequired() { + actions.andExpect(matchers.isProxyAuthenticationRequired()) + } + + /** + * @see StatusResultMatchers.isRequestTimeout + */ + fun isRequestTimeout() { + actions.andExpect(matchers.isRequestTimeout()) + } + + /** + * @see StatusResultMatchers.isConflict + */ + fun isConflict() { + actions.andExpect(matchers.isConflict()) + } + + /** + * @see StatusResultMatchers.isGone + */ + fun isGone() { + actions.andExpect(matchers.isGone()) + } + + /** + * @see StatusResultMatchers.isLengthRequired + */ + fun isLengthRequired() { + actions.andExpect(matchers.isLengthRequired()) + } + + /** + * @see StatusResultMatchers.isPreconditionFailed + */ + fun isPreconditionFailed() { + actions.andExpect(matchers.isPreconditionFailed()) + } + + /** + * @see StatusResultMatchers.isPayloadTooLarge + */ + fun isPayloadTooLarge() { + actions.andExpect(matchers.isPayloadTooLarge()) + } + + /** + * @see StatusResultMatchers.isUriTooLong + */ + fun isUriTooLong() { + actions.andExpect(matchers.isUriTooLong()) + } + + /** + * @see StatusResultMatchers.isUnsupportedMediaType + */ + fun isUnsupportedMediaType() { + actions.andExpect(matchers.isUnsupportedMediaType()) + } + + /** + * @see StatusResultMatchers.isRequestedRangeNotSatisfiable + */ + fun isRequestedRangeNotSatisfiable() { + actions.andExpect(matchers.isRequestedRangeNotSatisfiable()) + } + + /** + * @see StatusResultMatchers.isExpectationFailed + */ + fun isExpectationFailed() { + actions.andExpect(matchers.isExpectationFailed()) + } + + /** + * @see StatusResultMatchers.isIAmATeapot + */ + fun isIAmATeapot() { + actions.andExpect(matchers.isIAmATeapot()) + } + + /** + * @see StatusResultMatchers.isUnprocessableEntity + */ + fun isUnprocessableEntity() { + actions.andExpect(matchers.isUnprocessableEntity()) + } + + /** + * @see StatusResultMatchers.isLocked + */ + fun isLocked() { + actions.andExpect(matchers.isLocked()) + } + + /** + * @see StatusResultMatchers.isFailedDependency + */ + fun isFailedDependency() { + actions.andExpect(matchers.isFailedDependency()) + } + + /** + * @see StatusResultMatchers.isTooEarly + */ + fun isTooEarly() { + actions.andExpect(matchers.isTooEarly()) + } + + /** + * @see StatusResultMatchers.isUpgradeRequired + */ + fun isUpgradeRequired() { + actions.andExpect(matchers.isUpgradeRequired()) + } + + /** + * @see StatusResultMatchers.isPreconditionRequired + */ + fun isPreconditionRequired() { + actions.andExpect(matchers.isPreconditionRequired()) + } + + /** + * @see StatusResultMatchers.isTooManyRequests + */ + fun isTooManyRequests() { + actions.andExpect(matchers.isTooManyRequests()) + } + + /** + * @see StatusResultMatchers.isRequestHeaderFieldsTooLarge + */ + fun isRequestHeaderFieldsTooLarge() { + actions.andExpect(matchers.isRequestHeaderFieldsTooLarge()) + } + + /** + * @see StatusResultMatchers.isUnavailableForLegalReasons + */ + fun isUnavailableForLegalReasons() { + actions.andExpect(matchers.isUnavailableForLegalReasons()) + } + + /** + * @see StatusResultMatchers.isInternalServerError + */ + fun isInternalServerError() { + actions.andExpect(matchers.isInternalServerError()) + } + + /** + * @see StatusResultMatchers.isNotImplemented + */ + fun isNotImplemented() { + actions.andExpect(matchers.isNotImplemented()) + } + + /** + * @see StatusResultMatchers.isBadGateway + */ + fun isBadGateway() { + actions.andExpect(matchers.isBadGateway()) + } + + /** + * @see StatusResultMatchers.isServiceUnavailable + */ + fun isServiceUnavailable() { + actions.andExpect(matchers.isServiceUnavailable()) + } + + /** + * @see StatusResultMatchers.isGatewayTimeout + */ + fun isGatewayTimeout() { + actions.andExpect(matchers.isGatewayTimeout()) + } + + /** + * @see StatusResultMatchers.isHttpVersionNotSupported + */ + fun isHttpVersionNotSupported() { + actions.andExpect(matchers.isHttpVersionNotSupported()) + } + + /** + * @see StatusResultMatchers.isVariantAlsoNegotiates + */ + fun isVariantAlsoNegotiates() { + actions.andExpect(matchers.isVariantAlsoNegotiates()) + } + + /** + * @see StatusResultMatchers.isInsufficientStorage + */ + fun isInsufficientStorage() { + actions.andExpect(matchers.isInsufficientStorage()) + } + + /** + * @see StatusResultMatchers.isLoopDetected + */ + fun isLoopDetected() { + actions.andExpect(matchers.isLoopDetected()) + } + + /** + * @see StatusResultMatchers.isBandwidthLimitExceeded + */ + fun isBandwidthLimitExceeded() { + actions.andExpect(matchers.isBandwidthLimitExceeded()) + } + + /** + * @see StatusResultMatchers.isNotExtended + */ + fun isNotExtended() { + actions.andExpect(matchers.isNotExtended()) + } + + /** + * @see StatusResultMatchers.isNetworkAuthenticationRequired + */ + fun isNetworkAuthenticationRequired() { + actions.andExpect(matchers.isNetworkAuthenticationRequired()) + } +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ViewResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ViewResultMatchersDsl.kt new file mode 100644 index 0000000000..92638570b7 --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/ViewResultMatchersDsl.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions + +/** + * Provide a [ViewResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class ViewResultMatchersDsl internal constructor (private val actions: ResultActions) { + + private val matchers = MockMvcResultMatchers.view() + + /** + * @see ViewResultMatchers.name + */ + fun name(matcher: Matcher) { + actions.andExpect(matchers.name(matcher)) + } + + /** + * @see ViewResultMatchers.name + */ + fun name(expectedViewName: String) { + actions.andExpect(matchers.name(expectedViewName)) + } + +} diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/XpathResultMatchersDsl.kt b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/XpathResultMatchersDsl.kt new file mode 100644 index 0000000000..9f38d62998 --- /dev/null +++ b/spring-test/src/main/kotlin/org/springframework/test/web/servlet/result/XpathResultMatchersDsl.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2002-2020 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 + * + * https://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.result + +import org.hamcrest.Matcher +import org.springframework.test.web.servlet.ResultActions +import org.w3c.dom.Node +import org.w3c.dom.NodeList + +/** + * Provide a [XpathResultMatchers] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * + * @author Sebastien Deleuze + * @since 5.3 + */ +class XpathResultMatchersDsl internal constructor (private val actions: ResultActions, expression: String, namespaces: Map? = null, vararg args: Any?) { + + private val matchers = if (namespaces == null) MockMvcResultMatchers.xpath(expression, args) else MockMvcResultMatchers.xpath(expression, namespaces, args) + + /** + * @see XpathResultMatchers.node + */ + fun node(matcher: Matcher) { + actions.andExpect(matchers.node(matcher)) + } + + /** + * @see XpathResultMatchers.nodeList + */ + fun nodeList(matcher: Matcher) { + actions.andExpect(matchers.nodeList(matcher)) + } + + /** + * @see XpathResultMatchers.exists + */ + fun exists() { + actions.andExpect(matchers.exists()) + } + + /** + * @see XpathResultMatchers.doesNotExist + */ + fun doesNotExist() { + actions.andExpect(matchers.doesNotExist()) + } + + /** + * @see XpathResultMatchers.nodeCount + */ + fun nodeCount(matcher: Matcher) { + actions.andExpect(matchers.nodeCount(matcher)) + } + + /** + * @see XpathResultMatchers.nodeCount + */ + fun nodeCount(expectedCount: Int) { + actions.andExpect(matchers.nodeCount(expectedCount)) + } + + /** + * @see XpathResultMatchers.string + */ + fun string(matcher: Matcher) { + actions.andExpect(matchers.string(matcher)) + } + + /** + * @see XpathResultMatchers.string + */ + fun string(expectedValue: String) { + actions.andExpect(matchers.string(expectedValue)) + } + + /** + * @see XpathResultMatchers.number + */ + fun number(matcher: Matcher) { + actions.andExpect(matchers.number(matcher)) + } + + /** + * @see XpathResultMatchers.number + */ + fun number(expectedValue: Double) { + actions.andExpect(matchers.number(expectedValue)) + } + + /** + * @see XpathResultMatchers.booleanValue + */ + fun booleanValue(value: Boolean) { + actions.andExpect(matchers.booleanValue(value)) + } +} diff --git a/spring-test/src/test/kotlin/org/springframework/test/web/servlet/MockMvcExtensionsTests.kt b/spring-test/src/test/kotlin/org/springframework/test/web/servlet/MockMvcExtensionsTests.kt index 6324f197e6..72d750e413 100644 --- a/spring-test/src/test/kotlin/org/springframework/test/web/servlet/MockMvcExtensionsTests.kt +++ b/spring-test/src/test/kotlin/org/springframework/test/web/servlet/MockMvcExtensionsTests.kt @@ -18,6 +18,8 @@ package org.springframework.test.web.servlet import org.assertj.core.api.Assertions.* import org.hamcrest.CoreMatchers +import org.hamcrest.Matcher +import org.hamcrest.Matchers import org.junit.jupiter.api.Test import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus @@ -25,6 +27,7 @@ import org.springframework.http.MediaType.* import org.springframework.test.web.Person import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.web.bind.annotation.* +import org.springframework.web.servlet.ModelAndView import reactor.core.publisher.Mono import java.security.Principal import java.util.* @@ -50,7 +53,7 @@ class MockMvcExtensionsTests { } principal = Principal { "foo" } }.andExpect { - status { isOk } + status { isOk() } content { contentType(APPLICATION_JSON) } jsonPath("$.name") { value("Lee") } content { json("""{"someBoolean": false}""", false) } @@ -62,7 +65,7 @@ class MockMvcExtensionsTests { @Test fun `request without MockHttpServletRequestDsl`() { mockMvc.request(HttpMethod.GET, "/person/{name}", "Lee").andExpect { - status { isOk } + status { isOk() } }.andDo { print() } @@ -75,7 +78,7 @@ class MockMvcExtensionsTests { val matcher = ResultMatcher { matcherInvoked = true } val handler = ResultHandler { handlerInvoked = true } mockMvc.request(HttpMethod.GET, "/person/{name}", "Lee").andExpect { - status { isOk } + status { isOk() } }.andExpect { match(matcher) }.andDo { @@ -98,7 +101,7 @@ class MockMvcExtensionsTests { } principal = Principal { "foo" } }.andExpect { - status { isOk } + status { isOk() } content { contentType(APPLICATION_JSON) } jsonPath("$.name") { value("Lee") } content { json("""{"someBoolean": false}""", false) } @@ -117,7 +120,7 @@ class MockMvcExtensionsTests { } }.andExpect { status { - isCreated + isCreated() } } } @@ -139,7 +142,7 @@ class MockMvcExtensionsTests { assertThatExceptionOfType(AssertionError::class.java).isThrownBy { model { attributeExists("name", "wrong") } } assertThatExceptionOfType(AssertionError::class.java).isThrownBy { redirectedUrl("wrong/Url") } assertThatExceptionOfType(AssertionError::class.java).isThrownBy { redirectedUrlPattern("wrong/Url") } - assertThatExceptionOfType(AssertionError::class.java).isThrownBy { status { isAccepted } } + assertThatExceptionOfType(AssertionError::class.java).isThrownBy { status { isAccepted() } } assertThatExceptionOfType(AssertionError::class.java).isThrownBy { view { name("wrongName") } } assertThatExceptionOfType(AssertionError::class.java).isThrownBy { jsonPath("name") { value("wrong") } } } @@ -150,7 +153,7 @@ class MockMvcExtensionsTests { mockMvc.get("/person/Clint") { accept = APPLICATION_XML }.andExpect { - status { isOk } + status { isOk() } assertThatExceptionOfType(AssertionError::class.java).isThrownBy { xpath("//wrong") { nodeCount(1) } } }.andDo { print() @@ -160,7 +163,17 @@ class MockMvcExtensionsTests { @Test fun asyncDispatch() { mockMvc.get("/async").asyncDispatch().andExpect { - status { isOk } + status { isOk() } + } + } + + @Test + fun modelAndView() { + mockMvc.get("/").andExpect { + model { + assertThatExceptionOfType(AssertionError::class.java).isThrownBy { attribute("foo", "bar") } + attribute("foo", "foo") + } } } @@ -182,5 +195,8 @@ class MockMvcExtensionsTests { fun getAsync(): Mono { return Mono.just(Person("foo")) } + + @GetMapping("/") + fun index() = ModelAndView("index", mapOf("foo" to "foo", "bar" to "bar")) } }