Browse files

Add Spring MVC Test framework

This commit adds the spring-test-mvc project [1] to the Spring
Framework as part of the spring-test module.

The sources are added as a root-level project called "spring-test-mvc"
instead of under "spring-test" because the new sources need to be
compiled with Servlet 3 while the current "spring-test" sources require
Servlet 2.5 and the Eclipse IDE does not support having different
classpaths for the same project.

The Gradle build produces a single spring-test jar that contains
sources from both "spring-test" and "spring-test-mvc". This merge is
made possible through merge-dist.gradle as follows:

- jar tasks of the "from" project execute tasks of the "to" project
- "to" project is added to the classpath of the "from" project
- "to" project pom is updated with entries from the "from" project

For further details see documentation in merge-dist.gradle.

Special thanks to everyone who contributed to the initial development
of the Spring MVC Test framework:

 Arjen Poutsma <poutsma@mac.com>
 Craig Walls <cwalls@vmware.com>
 Frans Flippo <fransflippo@utopia.orange11.nl>
 Harry Lascelles <harry@firstbanco.com>
 Irfan <mail.urfi@gmail.com>
 Jörg Rathlev <joerg.rathlev@s24.com>
 Keesun Baik <whiteship2000@gmail.com>
 Keesun Baik <whiteship@epril.com>
 Matthew Reid <matthew.reid@nakedwines.com>
 Nils-Helge Garli Hegvik <Nils-Helge.Hegvik@telenor.com>
 Rob Winch <rwinch@vmware.com>
 Scott Frederick <sfrederick@vmware.com>
 Sven Filatov <sven.filatov@gmail.com>
 Thomas Bruyelle <thomas.bruyelle@gmail.com>
 youngm <youngm@gmail.com>

[1]: https://github.com/SpringSource/spring-test-mvc

Issue: SPR-9859, SPR-7951
  • Loading branch information...
1 parent 4812c18 commit 22bcb54ab66c952d1c122526729b64d77a77280b @rwinch rwinch committed with rstoyanchev Sep 28, 2012
Showing with 14,278 additions and 0 deletions.
  1. +42 −0 build.gradle
  2. +72 −0 merge-dist.gradle
  3. +1 −0 settings.gradle
  4. +79 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/AssertionErrors.java
  5. +207 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/MockRestServiceServer.java
  6. +39 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/RequestMatcher.java
  7. +79 −0 ...t-mvc/src/main/java/org/springframework/test/web/mock/client/RequestMatcherClientHttpRequest.java
  8. +39 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/ResponseActions.java
  9. +39 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/ResponseCreator.java
  10. +170 −0 ...test-mvc/src/main/java/org/springframework/test/web/mock/client/match/ContentRequestMatchers.java
  11. +128 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/client/match/JsonPathRequestMatchers.java
  12. +263 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/match/RequestMatchers.java
  13. +178 −0 ...g-test-mvc/src/main/java/org/springframework/test/web/mock/client/match/XpathRequestMatchers.java
  14. +23 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/match/package-info.java
  15. +21 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/package-info.java
  16. +132 −0 ...t-mvc/src/main/java/org/springframework/test/web/mock/client/response/DefaultResponseCreator.java
  17. +189 −0 ...ng-test-mvc/src/main/java/org/springframework/test/web/mock/client/response/ResponseCreators.java
  18. +23 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/response/package-info.java
  19. +99 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/DefaultMvcResult.java
  20. +167 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/MockMvc.java
  21. +35 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/MockMvcBuilder.java
  22. +77 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/MockMvcBuilderSupport.java
  23. +78 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/MvcResult.java
  24. +27 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/RequestBuilder.java
  25. +70 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/ResultActions.java
  26. +47 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/ResultHandler.java
  27. +48 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/ResultMatcher.java
  28. +153 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/TestDispatcherServlet.java
  29. +21 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/package-info.java
  30. +131 −0 ...ng-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/request/MockAsyncContext.java
  31. +694 −0 ...rc/main/java/org/springframework/test/web/mock/servlet/request/MockHttpServletRequestBuilder.java
  32. +121 −0 ...ava/org/springframework/test/web/mock/servlet/request/MockMultipartHttpServletRequestBuilder.java
  33. +107 −0 ...t-mvc/src/main/java/org/springframework/test/web/mock/servlet/request/MockMvcRequestBuilders.java
  34. +45 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/servlet/request/RequestPostProcessor.java
  35. +103 −0 ...c/main/java/org/springframework/test/web/mock/servlet/request/Servlet3MockHttpServletRequest.java
  36. +98 −0 ...va/org/springframework/test/web/mock/servlet/request/Servlet3MockMultipartHttpServletRequest.java
  37. +23 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/request/package-info.java
  38. +175 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/ContentResultMatchers.java
  39. +198 −0 ...test-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/CookieResultMatchers.java
  40. +87 −0 .../src/main/java/org/springframework/test/web/mock/servlet/result/FlashAttributeResultMatchers.java
  41. +113 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/HandlerResultMatchers.java
  42. +73 −0 ...test-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/HeaderResultMatchers.java
  43. +101 −0 ...st-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/JsonPathResultMatchers.java
  44. +66 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/MockMvcResultHandlers.java
  45. +185 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/MockMvcResultMatchers.java
  46. +175 −0 ...-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/ModelResultMatchers.java
  47. +200 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/PrintingResultHandler.java
  48. +153 −0 ...est-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/RequestResultMatchers.java
  49. +544 −0 ...test-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/StatusResultMatchers.java
  50. +63 −0 ...g-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/ViewResultMatchers.java
  51. +160 −0 ...-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/XpathResultMatchers.java
  52. +23 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/result/package-info.java
  53. +206 −0 ...test-mvc/src/main/java/org/springframework/test/web/mock/servlet/setup/DefaultMockMvcBuilder.java
  54. +74 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/setup/MockMvcBuilders.java
  55. +130 −0 ...-mvc/src/main/java/org/springframework/test/web/mock/servlet/setup/PatternMappingFilterProxy.java
  56. +382 −0 ...t-mvc/src/main/java/org/springframework/test/web/mock/servlet/setup/StandaloneMockMvcBuilder.java
  57. +343 −0 ...-mvc/src/main/java/org/springframework/test/web/mock/servlet/setup/StubWebApplicationContext.java
  58. +22 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/setup/package-info.java
  59. +122 −0 ...-test-mvc/src/main/java/org/springframework/test/web/mock/support/JsonPathExpectationsHelper.java
  60. +96 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/support/XmlExpectationsHelper.java
  61. +213 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/support/XpathExpectationsHelper.java
  62. +20 −0 spring-test-mvc/src/main/java/org/springframework/test/web/mock/support/package-info.java
  63. +88 −0 spring-test-mvc/src/test/java/org/springframework/test/web/mock/Person.java
  64. +102 −0 ...mvc/src/test/java/org/springframework/test/web/mock/client/MockClientHttpRequestFactoryTests.java
  65. +123 −0 ...mvc/src/test/java/org/springframework/test/web/mock/client/match/ContentRequestMatchersTests.java
  66. +93 −0 ...vc/src/test/java/org/springframework/test/web/mock/client/match/JsonPathRequestMatchersTests.java
  67. +139 −0 ...g-test-mvc/src/test/java/org/springframework/test/web/mock/client/match/RequestMatchersTests.java
  68. +113 −0 ...t-mvc/src/test/java/org/springframework/test/web/mock/client/match/XpathRequestMatchersTests.java
  69. +130 −0 ...st-mvc/src/test/java/org/springframework/test/web/mock/client/response/ResponseCreatorsTests.java
  70. +116 −0 spring-test-mvc/src/test/java/org/springframework/test/web/mock/client/samples/SampleTests.java
  71. +103 −0 ...st/java/org/springframework/test/web/mock/client/samples/matchers/ContentRequestMatcherTests.java
  72. +85 −0 ...est/java/org/springframework/test/web/mock/client/samples/matchers/HeaderRequestMatcherTests.java
  73. +150 −0 ...t/java/org/springframework/test/web/mock/client/samples/matchers/JsonPathRequestMatcherTests.java
  74. +133 −0 ...java/org/springframework/test/web/mock/client/samples/matchers/XmlContentRequestMatcherTests.java
  75. +233 −0 ...test/java/org/springframework/test/web/mock/client/samples/matchers/XpathRequestMatcherTests.java
  76. +123 −0 spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/StubMvcResult.java
  77. +390 −0 ...st/java/org/springframework/test/web/mock/servlet/request/MockHttpServletRequestBuilderTests.java
  78. +91 −0 ...vc/src/test/java/org/springframework/test/web/mock/servlet/result/ContentResultMatchersTests.java
  79. +56 −0 ...test/java/org/springframework/test/web/mock/servlet/result/FlashAttributeResultMatchersTests.java
  80. +92 −0 ...c/src/test/java/org/springframework/test/web/mock/servlet/result/JsonPathResultMatchersTests.java
  81. +141 −0 ...-mvc/src/test/java/org/springframework/test/web/mock/servlet/result/ModelResultMatchersTests.java
  82. +238 −0 ...vc/src/test/java/org/springframework/test/web/mock/servlet/result/PrintingResultHandlerTests.java
  83. +82 −0 ...mvc/src/test/java/org/springframework/test/web/mock/servlet/result/StatusResultMatchersTests.java
  84. +111 −0 ...-mvc/src/test/java/org/springframework/test/web/mock/servlet/result/XpathResultMatchersTests.java
  85. +94 −0 .../test/java/org/springframework/test/web/mock/servlet/samples/context/GenericWebContextLoader.java
  86. +63 −0 ...src/test/java/org/springframework/test/web/mock/servlet/samples/context/JavaTestContextTests.java
  87. +231 −0 ...java/org/springframework/test/web/mock/servlet/samples/context/SecurityRequestPostProcessors.java
  88. +135 −0 .../src/test/java/org/springframework/test/web/mock/servlet/samples/context/SpringSecurityTests.java
  89. +62 −0 ...g-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/WebConfig.java
  90. +24 −0 ...mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/WebContextLoader.java
  91. +66 −0 .../src/test/java/org/springframework/test/web/mock/servlet/samples/context/XmlTestContextTests.java
  92. +83 −0 ...st-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/standalone/AsyncTests.java
  93. +63 −0 ...test/java/org/springframework/test/web/mock/servlet/samples/standalone/ExceptionHandlerTests.java
  94. +171 −0 ...t-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/standalone/FilterTests.java
  95. +103 −0 ...mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/standalone/RedirectTests.java
  96. +110 −0 ...c/test/java/org/springframework/test/web/mock/servlet/samples/standalone/RequestBuilderTests.java
  97. +61 −0 ...test/java/org/springframework/test/web/mock/servlet/samples/standalone/RequestParameterTests.java
  98. +59 −0 ...src/test/java/org/springframework/test/web/mock/servlet/samples/standalone/ResponseBodyTests.java
  99. +160 −0 ...c/test/java/org/springframework/test/web/mock/servlet/samples/standalone/ViewResolutionTests.java
  100. +49 −0 ...framework/test/web/mock/servlet/samples/standalone/resulthandlers/PrintingResultHandlerTests.java
  101. +106 −0 ...pringframework/test/web/mock/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java
  102. +114 −0 ...springframework/test/web/mock/servlet/samples/standalone/resultmatchers/CookieAssertionTests.java
  103. +93 −0 ...amework/test/web/mock/servlet/samples/standalone/resultmatchers/FlashAttributeAssertionTests.java
  104. +85 −0 ...pringframework/test/web/mock/servlet/samples/standalone/resultmatchers/HandlerAssertionTests.java
  105. +102 −0 ...springframework/test/web/mock/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java
  106. +146 −0 ...ringframework/test/web/mock/servlet/samples/standalone/resultmatchers/JsonPathAssertionTests.java
  107. +124 −0 .../springframework/test/web/mock/servlet/samples/standalone/resultmatchers/ModelAssertionTests.java
  108. +77 −0 ...ework/test/web/mock/servlet/samples/standalone/resultmatchers/RequestAttributeAssertionTests.java
  109. +83 −0 ...ework/test/web/mock/servlet/samples/standalone/resultmatchers/SessionAttributeAssertionTests.java
  110. +103 −0 ...springframework/test/web/mock/servlet/samples/standalone/resultmatchers/StatusAssertionTests.java
  111. +68 −0 ...rg/springframework/test/web/mock/servlet/samples/standalone/resultmatchers/UrlAssertionTests.java
  112. +69 −0 ...ringframework/test/web/mock/servlet/samples/standalone/resultmatchers/ViewNameAssertionTests.java
  113. +123 −0 ...ngframework/test/web/mock/servlet/samples/standalone/resultmatchers/XmlContentAssertionTests.java
  114. +202 −0 .../springframework/test/web/mock/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java
  115. +272 −0 ...t/java/org/springframework/test/web/mock/servlet/setup/ConditionalDelegatingFilterProxyTests.java
  116. +82 −0 ...mvc/src/test/java/org/springframework/test/web/mock/servlet/setup/DefaultMockMvcBuilderTests.java
  117. +12 −0 spring-test-mvc/src/test/resources/META-INF/web-resources/WEB-INF/layouts/standardLayout.jsp
  118. +8 −0 spring-test-mvc/src/test/resources/META-INF/web-resources/WEB-INF/layouts/tiles.xml
  119. +2 −0 spring-test-mvc/src/test/resources/META-INF/web-resources/WEB-INF/views/home.jsp
  120. +10 −0 spring-test-mvc/src/test/resources/META-INF/web-resources/WEB-INF/views/tiles.xml
  121. +16 −0 spring-test-mvc/src/test/resources/META-INF/web-resources/resources/Spring.js
  122. +7 −0 spring-test-mvc/src/test/resources/log4j.properties
  123. +5 −0 spring-test-mvc/src/test/resources/org/springframework/test/web/mock/client/samples/ludwig.json
  124. +14 −0 .../resources/org/springframework/test/web/mock/servlet/samples/context/TestContextTests-context.xml
  125. +18 −0 ...est-mvc/src/test/resources/org/springframework/test/web/mock/servlet/samples/context/security.xml
  126. +31 −0 ...test-mvc/src/test/resources/org/springframework/test/web/mock/servlet/samples/servlet-context.xml
  127. +12 −0 spring-test-mvc/src/test/webapp/WEB-INF/layouts/main.jsp
  128. +8 −0 spring-test-mvc/src/test/webapp/WEB-INF/layouts/tiles.xml
  129. +2 −0 spring-test-mvc/src/test/webapp/WEB-INF/readme.txt
  130. +8 −0 spring-test-mvc/src/test/webapp/WEB-INF/views/tiles.xml
  131. +16 −0 spring-test-mvc/src/test/webapp/resources/Spring.js
  132. +2 −0 spring-test-mvc/test-mvc.gradle
View
42 build.gradle
@@ -548,6 +548,48 @@ project('spring-test') {
}
}
+project('spring-test-mvc') {
+ description = 'Spring Test MVC Framework'
+ apply from: 'test-mvc.gradle'
+ dependencies {
+ compile project(":spring-context")
+ compile project(":spring-webmvc")
+ compile project(":spring-test").sourceSets.main.output
+ compile("org.apache.tomcat:tomcat-servlet-api:7.0.8", provided)
+ compile "org.hamcrest:hamcrest-all:1.1"
+ compile("com.jayway.jsonpath:json-path:0.8.1", optional)
+ compile("xmlunit:xmlunit:1.2", optional)
+ testCompile("org.slf4j:jcl-over-slf4j:1.6.1")
+ testCompile("org.slf4j:slf4j-log4j12:1.6.1") {
+ exclude group: 'log4j', module: 'log4j'
+ }
+ testCompile("log4j:log4j:1.2.15") {
+ exclude group: 'javax.mail', module: 'mail'
+ exclude group: 'javax.jms', module: 'jms'
+ exclude group: 'com.sun.jdmk', module: 'jmxtools'
+ exclude group: 'com.sun.jmx', module: 'jmxri'
+ }
+ testCompile "javax.servlet:jstl:1.2"
+ testCompile "org.apache.tiles:tiles-jsp:2.2.2"
+ testCompile "org.hibernate:hibernate-validator:4.2.0.Final"
+ testCompile "org.codehaus.jackson:jackson-mapper-asl:1.4.2"
+ testCompile project(":spring-oxm")
+ testCompile "com.thoughtworks.xstream:xstream:1.3.1"
+ testCompile "cglib:cglib-nodep:2.2"
+ testCompile "rome:rome:1.0"
+ testCompile "javax.xml.bind:jaxb-api:2.2.6"
+ testCompile("org.springframework.security:spring-security-core:3.1.2.RELEASE") {
+ exclude group: 'org.springframework'
+ }
+ testCompile("org.springframework.security:spring-security-web:3.1.2.RELEASE") {
+ exclude group: 'org.springframework'
+ }
+ testCompile("org.springframework.security:spring-security-config:3.1.2.RELEASE") {
+ exclude group: 'org.springframework'
+ }
+ }
+}
+
project('spring-struts') {
description = 'Spring Struts'
dependencies {
View
72 merge-dist.gradle
@@ -0,0 +1,72 @@
+import org.gradle.plugins.ide.eclipse.model.ProjectDependency
+
+/**
+ * Will merge the distributions of the current project into mergeIntoProject. For
+ * example, to bundle spring-test-mvc in spring-test's jars. This script will perform the
+ * following steps:
+ * <ul>
+ * <li>Ensure that jar tasks of the project being merged from will execute the tasks of
+ * the project being merged into</li>
+ * <li>Add the project being merged into to the classpath of the project being merged
+ * from</li>
+ * <li>Update the pom.xml of the project being merged into to contain the entries from
+ * the project being merged from</li>
+ * </ul>
+ *
+ * Example Usage:
+ *
+ * ext.mergeIntoProject = project(':spring-test')
+ * apply from: "${rootProject.projectDir}/merge-dist.gradle"
+ */
+
+def mergeFromProject = project
+
+// invoking a task on mergeFromProject will invoke the task with the same name on mergeIntoProject
+def taskNamesToMerge = ['sourcesJar','jar','javadocJar','javadoc','install']
+taskNamesToMerge.each { taskName ->
+ def taskToRemove = tasks.getByPath(taskName)
+ taskToRemove.enabled = false
+ taskToRemove.dependsOn mergeIntoProject."$taskName"
+}
+
+// update mergeIntoProject artifacts to contain the mergeFromProject artifact contents
+mergeIntoProject."sourcesJar" {
+ from mergeFromProject.sourcesJar.source
+}
+mergeIntoProject."jar" {
+ from mergeFromProject.jar.source
+}
+mergeIntoProject."javadoc" {
+ source += mergeFromProject.javadoc.source
+ classpath += mergeFromProject.javadoc.classpath
+}
+
+// GRADLE-1116
+mergeFromProject.eclipse.classpath.file.whenMerged { classpath ->
+ classpath.entries.removeAll { entry -> entry.path.contains("/${mergeIntoProject.name}/build/") }
+ def dependency = new ProjectDependency("/${mergeIntoProject.name}", mergeIntoProject.path)
+ dependency.exported = true
+ classpath.entries.add(dependency)
+}
+
+// Update mergeIntoProject to contain additional configurations that contains all the dependencies from mergeFromProject
+// so that Maven pom generation works
+gradle.taskGraph.whenReady {
+ mergeFromProject.configurations.archives.artifacts.clear()
+
+ mergeFromProject.configurations.each { config->
+ def mapping = mergeFromProject.conf2ScopeMappings.getMapping([config])
+ if(mapping.scope) {
+ def newConfigName = mergeFromProject.name + "-"+ config.name
+ mergeIntoProject.configurations.add(newConfigName)
+ config.dependencies.each { dependency ->
+ mergeIntoProject.dependencies.add(newConfigName, dependency)
+ }
+ configure(mergeIntoProject.install.repositories.mavenInstaller.pom.scopeMappings) {
+ addMapping(mapping.priority + 100, mergeIntoProject.configurations."$newConfigName", mapping.scope)
+ }
+ mergeIntoProject.optionalDeps += mergeFromProject.optionalDeps
+ mergeIntoProject.providedDeps += mergeFromProject.providedDeps
+ }
+ }
+}
View
1 settings.gradle
@@ -15,6 +15,7 @@ include 'spring-orm'
include 'spring-oxm'
include 'spring-struts'
include 'spring-test'
+include 'spring-test-mvc'
include 'spring-tx'
include 'spring-web'
include 'spring-webmvc'
View
79 spring-test-mvc/src/main/java/org/springframework/test/web/mock/AssertionErrors.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2012 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.mock;
+
+/**
+ * JUnit independent assertion class.
+ *
+ * @author Lukas Krecan
+ * @author Arjen Poutsma
+ * @since 3.2
+ */
+public abstract class AssertionErrors {
+
+ private AssertionErrors() {
+ }
+
+ /**
+ * Fails a test with the given message.
+ *
+ * @param message the message
+ */
+ public static void fail(String message) {
+ throw new AssertionError(message);
+ }
+
+ /**
+ * Fails a test with the given message passing along expected and actual values to be added to the message.
+ *
+ * @param message the message
+ * @param expected the expected value
+ * @param actual the actual value
+ */
+ public static void fail(String message, Object expected, Object actual) {
+ throw new AssertionError(message + " expected:<" + expected + "> but was:<" + actual + ">");
+ }
+
+ /**
+ * Asserts that a condition is {@code true}. If not, throws an {@link AssertionError} with the given message.
+ *
+ * @param message the message
+ * @param condition the condition to test for
+ */
+ public static void assertTrue(String message, boolean condition) {
+ if (!condition) {
+ fail(message);
+ }
+ }
+
+ /**
+ * Asserts that two objects are equal. If not, an {@link AssertionError} is thrown with the given message.
+ *
+ * @param message the message
+ * @param expected the expected value
+ * @param actual the actual value
+ */
+ public static void assertEquals(String message, Object expected, Object actual) {
+ if (expected == null && actual == null) {
+ return;
+ }
+ if (expected != null && expected.equals(actual)) {
+ return;
+ }
+ fail(message, expected, actual);
+ }
+
+}
View
207 ...est-mvc/src/main/java/org/springframework/test/web/mock/client/MockRestServiceServer.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2002-2012 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.mock.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.test.web.mock.client.match.RequestMatchers;
+import org.springframework.test.web.mock.client.response.ResponseCreators;
+import org.springframework.util.Assert;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.client.support.RestGatewaySupport;
+
+/**
+ * <strong>Main entry point for client-side REST testing</strong>. Used for tests
+ * that involve direct or indirect (through client code) use of the
+ * {@link RestTemplate}. Provides a way to set up fine-grained expectations
+ * on the requests that will be performed through the {@code RestTemplate} and
+ * a way to define the responses to send back removing the need for an
+ * actual running server.
+ *
+ * <p>Below is an example:
+ * <pre>
+ * RestTemplate restTemplate = new RestTemplate()
+ * MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
+ *
+ * mockServer.expect(requestTo("/hotels/42")).andExpect(method(HttpMethod.GET))
+ * .andRespond(withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON));
+ *
+ * Hotel hotel = restTemplate.getForObject("/hotels/{id}", Hotel.class, 42);
+ * &#47;&#47; Use the hotel instance...
+ *
+ * mockServer.verify();
+ *
+ * <p>To create an instance of this class, use {@link #createServer(RestTemplate)}
+ * and provide the {@code RestTemplate} to set up for the mock testing.
+ *
+ * <p>After that use {@link #expect(RequestMatcher)} and fluent API methods
+ * {@link ResponseActions#andExpect(RequestMatcher) andExpect(RequestMatcher)} and
+ * {@link ResponseActions#andRespond(ResponseCreator) andRespond(ResponseCreator)}
+ * to set up request expectations and responses, most likely relying on the default
+ * {@code RequestMatcher} implementations provided in {@link RequestMatchers}
+ * and the {@code ResponseCreator} implementations provided in
+ * {@link ResponseCreators} both of which can be statically imported.
+ *
+ * <p>At the end of the test use {@link #verify()} to ensure all expected
+ * requests were actually performed.
+ *
+ * <p>Note that because of the fluent API offered by this class (and related
+ * classes), you can typically use the Code Completion features (i.e.
+ * ctrl-space) in your IDE to set up the mocks.
+ *
+ * <p><strong>Credits:</strong> The client-side REST testing support was
+ * inspired by and initially based on similar code in the Spring WS project for
+ * client-side tests involving the {@code WebServiceTemplate}.
+ *
+ * @author Craig Walls
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public class MockRestServiceServer {
+
+ private final List<RequestMatcherClientHttpRequest> expectedRequests =
+ new LinkedList<RequestMatcherClientHttpRequest>();
+
+ private final List<RequestMatcherClientHttpRequest> actualRequests =
+ new LinkedList<RequestMatcherClientHttpRequest>();
+
+
+ /**
+ * Private constructor.
+ * @see #createServer(RestTemplate)
+ * @see #createServer(RestGatewaySupport)
+ */
+ private MockRestServiceServer() {
+ }
+
+ /**
+ * Create a {@code MockRestServiceServer} and set up the given
+ * {@code RestTemplate} with a mock {@link ClientHttpRequestFactory}.
+ *
+ * @param restTemplate the RestTemplate to set up for mock testing
+ * @return the created mock server
+ */
+ public static MockRestServiceServer createServer(RestTemplate restTemplate) {
+ Assert.notNull(restTemplate, "'restTemplate' must not be null");
+
+ MockRestServiceServer mockServer = new MockRestServiceServer();
+ RequestMatcherClientHttpRequestFactory factory = mockServer.new RequestMatcherClientHttpRequestFactory();
+
+ restTemplate.setRequestFactory(factory);
+
+ return mockServer;
+ }
+
+ /**
+ * Create a {@code MockRestServiceServer} and set up the given
+ * {@code RestGatewaySupport} with a mock {@link ClientHttpRequestFactory}.
+ *
+ * @param restGateway the REST gateway to set up for mock testing
+ * @return the created mock server
+ */
+ public static MockRestServiceServer createServer(RestGatewaySupport restGateway) {
+ Assert.notNull(restGateway, "'gatewaySupport' must not be null");
+ return createServer(restGateway.getRestTemplate());
+ }
+
+ /**
+ * Set up a new HTTP request expectation. The returned {@link ResponseActions}
+ * is used to set up further expectations and to define the response.
+ *
+ * <p>This method may be invoked multiple times before starting the test, i.e.
+ * before using the {@code RestTemplate}, to set up expectations for multiple
+ * requests.
+ *
+ * @param requestMatcher a request expectation, see {@link RequestMatchers}
+ * @return used to set up further expectations or to define a response
+ */
+ public ResponseActions expect(RequestMatcher requestMatcher) {
+ Assert.state(this.actualRequests.isEmpty(), "Can't add more expected requests with test already underway");
+ RequestMatcherClientHttpRequest request = new RequestMatcherClientHttpRequest(requestMatcher);
+ this.expectedRequests.add(request);
+ return request;
+ }
+
+ /**
+ * Verify that all expected requests set up via
+ * {@link #expect(RequestMatcher)} were indeed performed.
+ *
+ * @throws AssertionError when some expectations were not met
+ */
+ public void verify() {
+ if (this.expectedRequests.isEmpty() || this.expectedRequests.equals(this.actualRequests)) {
+ return;
+ }
+ throw new AssertionError(getVerifyMessage());
+ }
+
+ private String getVerifyMessage() {
+ StringBuilder sb = new StringBuilder("Further request(s) expected\n");
+
+ if (this.actualRequests.size() > 0) {
+ sb.append("The following ");
+ }
+ sb.append(this.actualRequests.size()).append(" out of ");
+ sb.append(this.expectedRequests.size()).append(" were executed");
+
+ if (this.actualRequests.size() > 0) {
+ sb.append(":\n");
+ for (RequestMatcherClientHttpRequest request : this.actualRequests) {
+ sb.append(request.toString()).append("\n");
+ }
+ }
+
+ return sb.toString();
+ }
+
+
+ /**
+ * Mock ClientHttpRequestFactory that creates requests by iterating
+ * over the list of expected {@link RequestMatcherClientHttpRequest}'s.
+ */
+ private class RequestMatcherClientHttpRequestFactory implements ClientHttpRequestFactory {
+
+ private Iterator<RequestMatcherClientHttpRequest> requestIterator;
+
+ public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
+ Assert.notNull(uri, "'uri' must not be null");
+ Assert.notNull(httpMethod, "'httpMethod' must not be null");
+
+ if (this.requestIterator == null) {
+ this.requestIterator = MockRestServiceServer.this.expectedRequests.iterator();
+ }
+ if (!this.requestIterator.hasNext()) {
+ throw new AssertionError("No further requests expected");
+ }
+
+ RequestMatcherClientHttpRequest request = this.requestIterator.next();
+ request.setURI(uri);
+ request.setMethod(httpMethod);
+
+ MockRestServiceServer.this.actualRequests.add(request);
+
+ return request;
+ }
+ }
+
+}
View
39 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/RequestMatcher.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2012 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.mock.client;
+
+import java.io.IOException;
+
+import org.springframework.http.client.ClientHttpRequest;
+
+/**
+ * A contract for matching requests to expectations.
+ *
+ * @author Craig Walls
+ * @since 3.2
+ */
+public interface RequestMatcher {
+
+ /**
+ * Match the given request against some expectations.
+ *
+ * @param request the request to make assertions on
+ * @throws IOException in case of I/O errors
+ * @throws AssertionError if expectations are not met
+ */
+ void match(ClientHttpRequest request) throws IOException, AssertionError;
+
+}
View
79 ...c/main/java/org/springframework/test/web/mock/client/RequestMatcherClientHttpRequest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2012 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.mock.client;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.util.Assert;
+
+/**
+ * A specialization of {@code MockClientHttpRequest} that matches the request
+ * against a set of expectations, via {@link RequestMatcher} instances. The
+ * expectations are checked when the request is executed. This class also uses a
+ * {@link ResponseCreator} to create the response.
+ *
+ * @author Craig Walls
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+class RequestMatcherClientHttpRequest extends MockClientHttpRequest implements ResponseActions {
+
+ private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
+
+ private ResponseCreator responseCreator;
+
+
+ public RequestMatcherClientHttpRequest(RequestMatcher requestMatcher) {
+ Assert.notNull(requestMatcher, "RequestMatcher is required");
+ this.requestMatchers.add(requestMatcher);
+ }
+
+ public ResponseActions andExpect(RequestMatcher requestMatcher) {
+ Assert.notNull(requestMatcher, "RequestMatcher is required");
+ this.requestMatchers.add(requestMatcher);
+ return this;
+ }
+
+ public void andRespond(ResponseCreator responseCreator) {
+ Assert.notNull(responseCreator, "ResponseCreator is required");
+ this.responseCreator = responseCreator;
+ }
+
+ public ClientHttpResponse execute() throws IOException {
+
+ if (this.requestMatchers.isEmpty()) {
+ throw new AssertionError("No request expectations to execute");
+ }
+
+ if (this.responseCreator == null) {
+ throw new AssertionError("No ResponseCreator was set up. Add it after request expectations, "
+ + "e.g. MockRestServiceServer.expect(requestTo(\"/foo\")).andRespond(withSuccess())");
+ }
+
+ for (RequestMatcher requestMatcher : this.requestMatchers) {
+ requestMatcher.match(this);
+ }
+
+ setResponse(this.responseCreator.createResponse(this));
+
+ return super.execute();
+ }
+
+}
View
39 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/ResponseActions.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2012 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.mock.client;
+
+/**
+ * A contract for setting up request expectations and defining a response.
+ * Implementations can be obtained through {@link MockRestServiceServer#expect(RequestMatcher)}.
+ *
+ * @author Craig Walls
+ * @since 3.2
+ */
+public interface ResponseActions {
+
+ /**
+ * Add a request expectation.
+ * @return the expectation
+ */
+ ResponseActions andExpect(RequestMatcher requestMatcher);
+
+ /**
+ * Define the response.
+ * @param responseCreator the creator of the response
+ */
+ void andRespond(ResponseCreator responseCreator);
+
+}
View
39 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/ResponseCreator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2012 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.mock.client;
+
+import java.io.IOException;
+
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.test.web.mock.client.response.ResponseCreators;
+
+/**
+ * A contract for creating a {@link ClientHttpResponse}.
+ * Implementations can be obtained via {@link ResponseCreators}.
+ *
+ * @author Craig Walls
+ * @since 3.2
+ */
+public interface ResponseCreator {
+
+ /**
+ * Create a response for the given request.
+ * @param request the request
+ */
+ ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException;
+
+}
View
170 .../src/main/java/org/springframework/test/web/mock/client/match/ContentRequestMatchers.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2002-2012 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.mock.client.match;
+
+import static org.springframework.test.web.mock.AssertionErrors.assertEquals;
+import static org.springframework.test.web.mock.AssertionErrors.assertTrue;
+
+import java.io.IOException;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.mock.client.RequestMatcher;
+import org.springframework.test.web.mock.support.XmlExpectationsHelper;
+import org.w3c.dom.Node;
+
+/**
+ * Factory for request content {@code RequestMatcher}'s. An instance of this
+ * class is typically accessed via {@link RequestMatchers#content()}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public class ContentRequestMatchers {
+
+ private final XmlExpectationsHelper xmlHelper;
+
+
+ /**
+ * Class constructor, not for direct instantiation.
+ * Use {@link RequestMatchers#content()}.
+ */
+ protected ContentRequestMatchers() {
+ this.xmlHelper = new XmlExpectationsHelper();
+ }
+
+ /**
+ * Assert the request content type as a String.
+ */
+ public RequestMatcher mimeType(String expectedContentType) {
+ return mimeType(MediaType.parseMediaType(expectedContentType));
+ }
+
+ /**
+ * Assert the request content type as a {@link MediaType}.
+ */
+ public RequestMatcher mimeType(final MediaType expectedContentType) {
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ MediaType actualContentType = request.getHeaders().getContentType();
+ assertTrue("Content type not set", actualContentType != null);
+ assertEquals("Content type", expectedContentType, actualContentType);
+ }
+ };
+ }
+
+ /**
+ * Get the body of the request as a UTF-8 string and appply the given {@link Matcher}.
+ */
+ public RequestMatcher string(final Matcher<? super String> matcher) {
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ MatcherAssert.assertThat("Request content", mockRequest.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Get the body of the request as a UTF-8 string and compare it to the given String.
+ */
+ public RequestMatcher string(String expectedContent) {
+ return string(Matchers.equalTo(expectedContent));
+ }
+
+ /**
+ * Compare the body of the request to the given byte array.
+ */
+ public RequestMatcher bytes(final byte[] expectedContent) {
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ byte[] content = mockRequest.getBodyAsBytes();
+ MatcherAssert.assertThat("Request content", content, Matchers.equalTo(expectedContent));
+ }
+ };
+ }
+
+ /**
+ * Parse the request body and the given String as XML and assert that the
+ * two are "similar" - i.e. they contain the same elements and attributes
+ * regardless of order.
+ *
+ * <p>Use of this matcher assumes the
+ * <a href="http://xmlunit.sourceforge.net/">XMLUnit<a/> library is available.
+ *
+ * @param expectedXmlContent the expected XML content
+ */
+ public RequestMatcher xml(final String expectedXmlContent) {
+ return new AbstractXmlRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xmlHelper.assertXmlEqual(expectedXmlContent, request.getBodyAsString());
+ }
+ };
+ }
+
+ /**
+ * Parse the request content as {@link Node} and apply the given {@link Matcher}.
+ */
+ public RequestMatcher node(final Matcher<? super Node> matcher) {
+ return new AbstractXmlRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xmlHelper.assertNode(request.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Parse the request content as {@link DOMSource} and apply the given {@link Matcher}.
+ * @see <a href="http://code.google.com/p/xml-matchers/">http://code.google.com/p/xml-matchers/</a>
+ */
+ public RequestMatcher source(final Matcher<? super Source> matcher) {
+ return new AbstractXmlRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xmlHelper.assertSource(request.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Abstract base class for XML {@link RequestMatcher}'s.
+ */
+ private abstract static class AbstractXmlRequestMatcher implements RequestMatcher {
+
+ public final void match(ClientHttpRequest request) throws IOException, AssertionError {
+ try {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ matchInternal(mockRequest);
+ }
+ catch (Exception e) {
+ throw new AssertionError("Failed to parse expected or actual XML request content: " + e.getMessage());
+ }
+ }
+
+ protected abstract void matchInternal(MockClientHttpRequest request) throws Exception;
+
+ }
+}
View
128 ...src/main/java/org/springframework/test/web/mock/client/match/JsonPathRequestMatchers.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2002-2012 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.mock.client.match;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.List;
+
+import org.hamcrest.Matcher;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.mock.client.RequestMatcher;
+import org.springframework.test.web.mock.support.JsonPathExpectationsHelper;
+
+/**
+ * Factory methods for request content {@code RequestMatcher}'s using a <a
+ * href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression.
+ * An instance of this class is typically accessed via
+ * {@code RequestMatchers.jsonPath(..)}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public class JsonPathRequestMatchers {
+
+ private JsonPathExpectationsHelper jsonPathHelper;
+
+
+ /**
+ * Class constructor, not for direct instantiation. Use
+ * {@link RequestMatchers#jsonPath(String, Matcher)} or
+ * {@link RequestMatchers#jsonPath(String, Object...)}.
+ *
+ * @param expression the JSONPath expression
+ * @param args arguments to parameterize the JSONPath expression with using
+ * the formatting specifiers defined in
+ * {@link String#format(String, Object...)}
+ */
+ protected JsonPathRequestMatchers(String expression, Object ... args) {
+ this.jsonPathHelper = new JsonPathExpectationsHelper(expression, args);
+ }
+
+ /**
+ * Evaluate the JSONPath and assert the resulting value with the given {@code Matcher}.
+ */
+ public <T> RequestMatcher value(final Matcher<T> matcher) {
+ return new AbstractJsonPathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
+ jsonPathHelper.assertValue(request.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Apply the JSONPath and assert the resulting value.
+ */
+ public RequestMatcher value(Object expectedValue) {
+ return value(equalTo(expectedValue));
+ }
+
+ /**
+ * Apply the JSONPath and assert the resulting value.
+ */
+ public RequestMatcher exists() {
+ return new AbstractJsonPathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
+ jsonPathHelper.exists(request.getBodyAsString());
+ }
+ };
+ }
+
+ /**
+ * Evaluate the JSON path and assert the resulting content exists.
+ */
+ public RequestMatcher doesNotExist() {
+ return new AbstractJsonPathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
+ jsonPathHelper.doesNotExist(request.getBodyAsString());
+ }
+ };
+ }
+
+ /**
+ * Assert the content at the given JSONPath is an array.
+ */
+ public RequestMatcher isArray() {
+ return value(instanceOf(List.class));
+ }
+
+
+ /**
+ * Abstract base class for JSONPath {@link RequestMatcher}'s.
+ */
+ private abstract static class AbstractJsonPathRequestMatcher implements RequestMatcher {
+
+ public final void match(ClientHttpRequest request) throws IOException, AssertionError {
+ try {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ matchInternal(mockRequest);
+ }
+ catch (ParseException e) {
+ throw new AssertionError("Failed to parse JSON request content: " + e.getMessage());
+ }
+ }
+
+ protected abstract void matchInternal(MockClientHttpRequest request) throws IOException, ParseException;
+
+ }
+}
View
263 ...est-mvc/src/main/java/org/springframework/test/web/mock/client/match/RequestMatchers.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2002-2012 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.mock.client.match;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathExpressionException;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.hamcrest.core.IsEqual;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.mock.AssertionErrors;
+import org.springframework.test.web.mock.client.MockRestServiceServer;
+import org.springframework.test.web.mock.client.RequestMatcher;
+import org.springframework.util.Assert;
+
+/**
+ * Static, factory methods for {@link RequestMatcher} classes. Typically used to
+ * provide input for {@link MockRestServiceServer#expect(RequestMatcher)}.
+ *
+ * <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
+ * favorite. To navigate, open the Preferences and type "favorites".
+ *
+ * @author Craig Walls
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public abstract class RequestMatchers {
+
+
+ /**
+ * Private class constructor.
+ */
+ private RequestMatchers() {
+ }
+
+ /**
+ * Match to any request.
+ */
+ public static RequestMatcher anything() {
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws AssertionError {
+ }
+ };
+ }
+
+ /**
+ * Assert the request URI string with the given matcher.
+ *
+ * @param matcher String matcher for the expected URI
+ * @return the request matcher
+ */
+ public static RequestMatcher requestTo(final Matcher<String> matcher) {
+ Assert.notNull(matcher, "'matcher' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ MatcherAssert.assertThat("Request URI", request.getURI().toString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the request URI string.
+ *
+ * @param uri the expected URI
+ * @return the request matcher
+ */
+ public static RequestMatcher requestTo(String uri) {
+ Assert.notNull(uri, "'uri' must not be null");
+ return requestTo(Matchers.equalTo(uri));
+ }
+
+ /**
+ * Assert the {@link HttpMethod} of the request.
+ *
+ * @param method the HTTP method
+ * @return the request matcher
+ */
+ public static RequestMatcher method(final HttpMethod method) {
+ Assert.notNull(method, "'method' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws AssertionError {
+ AssertionErrors.assertEquals("Unexpected HttpMethod", method, request.getMethod());
+ }
+ };
+ }
+
+ /**
+ * Expect a request to the given URI.
+ *
+ * @param uri the expected URI
+ * @return the request matcher
+ */
+ public static RequestMatcher requestTo(final URI uri) {
+ Assert.notNull(uri, "'uri' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws IOException, AssertionError {
+ AssertionErrors.assertEquals("Unexpected request", uri, request.getURI());
+ }
+ };
+ }
+
+ /**
+ * Assert request header values with the given Hamcrest matcher.
+ */
+ public static RequestMatcher header(final String name, final Matcher<? super String>... matchers) {
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) {
+ HttpHeaders headers = request.getHeaders();
+ List<String> values = headers.get(name);
+ AssertionErrors.assertTrue("Expected header <" + name + ">", values != null);
+ AssertionErrors.assertTrue("Expected header <" + name + "> to have at least <" + matchers.length
+ + "> values but it has only <" + values.size() + ">", matchers.length <= values.size());
+ for (int i = 0 ; i < matchers.length; i++) {
+ MatcherAssert.assertThat("Request header", headers.get(name).get(i), matchers[i]);
+ }
+ }
+ };
+ }
+
+ /**
+ * Assert request header values.
+ */
+ public static RequestMatcher header(String name, String... values) {
+ @SuppressWarnings("unchecked")
+ Matcher<? super String>[] matchers = new IsEqual[values.length];
+ for (int i = 0; i < values.length; i++) {
+ matchers[i] = Matchers.equalTo(values[i]);
+ }
+ return header(name, matchers);
+ }
+
+ /**
+ * Access to request body matchers.
+ */
+ public static ContentRequestMatchers content() {
+ return new ContentRequestMatchers();
+ }
+
+ /**
+ * Access to request body matchers using a <a
+ * href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression to
+ * inspect a specific subset of the body. The JSON path expression can be a
+ * parameterized string using formatting specifiers as defined in
+ * {@link String#format(String, Object...)}.
+ *
+ * @param expression the JSON path optionally parameterized with arguments
+ * @param args arguments to parameterize the JSON path expression with
+ */
+ public static JsonPathRequestMatchers jsonPath(String expression, Object ... args) {
+ return new JsonPathRequestMatchers(expression, args);
+ }
+
+ /**
+ * Access to request body matchers using a <a
+ * href="http://goessner.net/articles/JsonPath/">JSONPath</a> expression to
+ * inspect a specific subset of the body and a Hamcrest match for asserting
+ * the value found at the JSON path.
+ *
+ * @param expression the JSON path expression
+ * @param matcher a matcher for the value expected at the JSON path
+ */
+ public static <T> RequestMatcher jsonPath(String expression, Matcher<T> matcher) {
+ return new JsonPathRequestMatchers(expression).value(matcher);
+ }
+
+ /**
+ * Access to request body matchers using an XPath to inspect a specific
+ * subset of the body. The XPath expression can be a parameterized string
+ * using formatting specifiers as defined in
+ * {@link String#format(String, Object...)}.
+ *
+ * @param expression the XPath optionally parameterized with arguments
+ * @param args arguments to parameterize the XPath expression with
+ */
+ public static XpathRequestMatchers xpath(String expression, Object... args) throws XPathExpressionException {
+ return new XpathRequestMatchers(expression, null, args);
+ }
+
+ /**
+ * Access to response body matchers using an XPath to inspect a specific
+ * subset of the body. The XPath expression can be a parameterized string
+ * using formatting specifiers as defined in
+ * {@link String#format(String, Object...)}.
+ *
+ * @param expression the XPath optionally parameterized with arguments
+ * @param namespaces namespaces referenced in the XPath expression
+ * @param args arguments to parameterize the XPath expression with
+ */
+ public static XpathRequestMatchers xpath(String expression, Map<String, String> namespaces, Object... args)
+ throws XPathExpressionException {
+
+ return new XpathRequestMatchers(expression, namespaces, args);
+ }
+
+
+ // Deprecated methods ..
+
+ /**
+ * Expect that the specified request header contains a subtring
+ *
+ * @deprecated in favor of {@link #header(String, Matcher...)}
+ */
+ public static RequestMatcher headerContains(final String header, final String substring) {
+ Assert.notNull(header, "'header' must not be null");
+ Assert.notNull(substring, "'substring' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws AssertionError {
+ List<String> actualHeaders = request.getHeaders().get(header);
+ AssertionErrors.assertTrue("Expected header <" + header + "> in request", actualHeaders != null);
+
+ boolean foundMatch = false;
+ for (String headerValue : actualHeaders) {
+ if (headerValue.contains(substring)) {
+ foundMatch = true;
+ break;
+ }
+ }
+
+ AssertionErrors.assertTrue("Expected value containing <" + substring + "> in header <" + header + ">",
+ foundMatch);
+ }
+ };
+ }
+
+ /**
+ * Expect the given request body content.
+ *
+ * @deprecated in favor of {@link #content()} as well as {@code jsonPath(..)},
+ * and {@code xpath(..)} methods in this class
+ */
+ public static RequestMatcher body(final String body) {
+ Assert.notNull(body, "'body' must not be null");
+ return new RequestMatcher() {
+ public void match(ClientHttpRequest request) throws AssertionError, IOException {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ AssertionErrors.assertEquals("Unexpected body content", body, mockRequest.getBodyAsString());
+ }
+ };
+ }
+
+}
View
178 ...vc/src/main/java/org/springframework/test/web/mock/client/match/XpathRequestMatchers.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2002-2012 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.mock.client.match;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.xml.xpath.XPathExpressionException;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.test.web.mock.client.RequestMatcher;
+import org.springframework.test.web.mock.support.XpathExpectationsHelper;
+import org.w3c.dom.Node;
+
+/**
+ * Factory methods for request content {@code RequestMatcher}'s using an XPath
+ * expression. An instance of this class is typically accessed via
+ * {@code RequestMatchers.xpath(..)}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public class XpathRequestMatchers {
+
+ private final XpathExpectationsHelper xpathHelper;
+
+
+ /**
+ * Class constructor, not for direct instantiation. Use
+ * {@link RequestMatchers#xpath(String, Object...)} or
+ * {@link RequestMatchers#xpath(String, Map, Object...)}.
+ *
+ * @param expression the XPath expression
+ * @param namespaces XML namespaces referenced in the XPath expression, or {@code null}
+ * @param args arguments to parameterize the XPath expression with using the
+ * formatting specifiers defined in {@link String#format(String, Object...)}
+ *
+ * @throws XPathExpressionException
+ */
+ protected XpathRequestMatchers(String expression, Map<String, String> namespaces, Object ... args)
+ throws XPathExpressionException {
+
+ this.xpathHelper = new XpathExpectationsHelper(expression, namespaces, args);
+ }
+
+ /**
+ * Apply the XPath and assert it with the given {@code Matcher<Node>}.
+ */
+ public <T> RequestMatcher node(final Matcher<? super Node> matcher) {
+ return new AbstractXpathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xpathHelper.assertNode(request.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert that content exists at the given XPath.
+ */
+ public <T> RequestMatcher exists() {
+ return node(Matchers.notNullValue());
+ }
+
+ /**
+ * Assert that content does not exist at the given XPath.
+ */
+ public <T> RequestMatcher doesNotExist() {
+ return node(Matchers.nullValue());
+ }
+
+ /**
+ * Apply the XPath and assert the number of nodes found with the given
+ * {@code Matcher<Integer>}.
+ */
+ public <T> RequestMatcher nodeCount(final Matcher<Integer> matcher) {
+ return new AbstractXpathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xpathHelper.assertNodeCount(request.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Apply the XPath and assert the number of nodes found.
+ */
+ public <T> RequestMatcher nodeCount(int expectedCount) {
+ return nodeCount(Matchers.equalTo(expectedCount));
+ }
+
+ /**
+ * Apply the XPath and assert the String content found with the given matcher.
+ */
+ public <T> RequestMatcher string(final Matcher<? super String> matcher) {
+ return new AbstractXpathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xpathHelper.assertString(request.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Apply the XPath and assert the String content found.
+ */
+ public RequestMatcher string(String value) {
+ return string(Matchers.equalTo(value));
+ }
+
+ /**
+ * Apply the XPath and assert the number found with the given matcher.
+ */
+ public <T> RequestMatcher number(final Matcher<? super Double> matcher) {
+ return new AbstractXpathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xpathHelper.assertNumber(request.getBodyAsString(), matcher);
+ }
+ };
+ }
+
+ /**
+ * Apply the XPath and assert the number of nodes found.
+ */
+ public RequestMatcher number(Double value) {
+ return number(Matchers.equalTo(value));
+ }
+
+ /**
+ * Apply the XPath and assert the boolean value found.
+ */
+ public <T> RequestMatcher booleanValue(final Boolean value) {
+ return new AbstractXpathRequestMatcher() {
+ @Override
+ protected void matchInternal(MockClientHttpRequest request) throws Exception {
+ xpathHelper.assertBoolean(request.getBodyAsString(), value);
+ }
+ };
+ }
+
+
+ /**
+ * Abstract base class for XPath {@link RequestMatcher}'s.
+ */
+ private abstract static class AbstractXpathRequestMatcher implements RequestMatcher {
+
+ public final void match(ClientHttpRequest request) throws IOException, AssertionError {
+ try {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ matchInternal(mockRequest);
+ }
+ catch (Exception e) {
+ throw new AssertionError("Failed to parse XML request content: " + e.getMessage());
+ }
+ }
+
+ protected abstract void matchInternal(MockClientHttpRequest request) throws Exception;
+
+ }
+
+}
View
23 ...g-test-mvc/src/main/java/org/springframework/test/web/mock/client/match/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2002-2012 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.
+ */
+
+/**
+ * Contains built-in {@link org.springframework.test.web.mock.client.RequestMatcher}
+ * implementations. Use
+ * {@link org.springframework.test.web.mock.client.match.RequestMatchers}
+ * to gain access to instances of those implementations.
+ */
+package org.springframework.test.web.mock.client.match;
View
21 spring-test-mvc/src/main/java/org/springframework/test/web/mock/client/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2002-2012 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.
+ */
+
+/**
+ * Contains client-side REST testing support.
+ * @see org.springframework.test.web.mock.client.MockRestServiceServer
+ */
+package org.springframework.test.web.mock.client;
View
132 ...c/main/java/org/springframework/test/web/mock/client/response/DefaultResponseCreator.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2002-2012 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.mock.client.response;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.mock.http.client.MockClientHttpResponse;
+import org.springframework.test.web.mock.client.ResponseCreator;
+import org.springframework.util.Assert;
+
+/**
+ * A {@code ResponseCreator} with builder-style methods for adding response details.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public class DefaultResponseCreator implements ResponseCreator {
+
+ private byte[] content;
+
+ private Resource contentResource;
+
+ private final HttpHeaders headers = new HttpHeaders();
+
+ private HttpStatus statusCode;
+
+
+ /**
+ * Protected constructor.
+ * Use static factory methods in {@link ResponseCreators}.
+ */
+ protected DefaultResponseCreator(HttpStatus statusCode) {
+ Assert.notNull(statusCode);
+ this.statusCode = statusCode;
+ }
+
+ public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
+ MockClientHttpResponse response;
+ if (this.contentResource != null ){
+ InputStream stream = this.contentResource.getInputStream();
+ response = new MockClientHttpResponse(stream, this.statusCode);
+ }
+ else {
+ response = new MockClientHttpResponse(this.content, this.statusCode);
+ }
+ response.getHeaders().putAll(this.headers);
+ return response;
+ }
+
+ /**
+ * Set the body as a UTF-8 String.
+ */
+ public DefaultResponseCreator body(String content) {
+ try {
+ this.content = content.getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException e) {
+ // should not happen, UTF-8 is always supported
+ throw new IllegalStateException(e);
+ }
+ return this;
+ }
+
+ /**
+ * Set the body as a byte array.
+ */
+ public DefaultResponseCreator body(byte[] content) {
+ this.content = content;
+ return this;
+ }
+
+ /**
+ * Set the body as a {@link Resource}.
+ */
+ public DefaultResponseCreator body(Resource resource) {
+ this.contentResource = resource;
+ return this;
+ }
+
+ /**
+ * Set the {@code Content-Type} header.
+ */
+ public DefaultResponseCreator contentType(MediaType mediaType) {
+ if (mediaType != null) {
+ this.headers.setContentType(mediaType);
+ }
+ return this;
+ }
+
+ /**
+ * Set the {@code Location} header.
+ */
+ public DefaultResponseCreator location(URI location) {
+ this.headers.setLocation(location);
+ return this;
+ }
+
+ /**
+ * Copy all given headers.
+ */
+ public DefaultResponseCreator headers(HttpHeaders headers) {
+ for (String headerName : headers.keySet()) {
+ for (String headerValue : headers.get(headerName)) {
+ this.headers.add(headerName, headerValue);
+ }
+ }
+ return this;
+ }
+
+}
View
189 ...mvc/src/main/java/org/springframework/test/web/mock/client/response/ResponseCreators.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2002-2012 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.mock.client.response;
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockClientHttpResponse;
+import org.springframework.test.web.mock.client.ResponseCreator;
+
+/**
+ * Static factory methods for obtaining a {@link ResponseCreator} instance.
+ *
+ * <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
+ * favorite. To navigate, open the Preferences and type "favorites".
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public abstract class ResponseCreators {
+
+
+ private ResponseCreators() {
+ }
+
+ /**
+ * {@code ResponseCreator} for a 200 response (OK).
+ */
+ public static DefaultResponseCreator withSuccess() {
+ return new DefaultResponseCreator(HttpStatus.OK);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 200 response (OK) with String body.
+ * @param body the response body, a "UTF-8" string
+ * @param mediaType the type of the content, may be {@code null}
+ */
+ public static DefaultResponseCreator withSuccess(String body, MediaType mediaType) {
+ return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(mediaType);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 200 response (OK) with byte[] body.
+ * @param body the response body
+ * @param mediaType the type of the content, may be {@code null}
+ */
+ public static DefaultResponseCreator withSuccess(byte[] body, MediaType contentType) {
+ return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(contentType);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 200 response (OK) content with {@link Resource}-based body.
+ * @param body the response body
+ * @param mediaType the type of the content, may be {@code null}
+ */
+ public static DefaultResponseCreator withSuccess(Resource body, MediaType contentType) {
+ return new DefaultResponseCreator(HttpStatus.OK).body(body).contentType(contentType);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 201 response (CREATED) with a 'Location' header.
+ * @param location the value for the {@code Location} header
+ */
+ public static DefaultResponseCreator withCreatedEntity(URI location) {
+ return new DefaultResponseCreator(HttpStatus.CREATED).location(location);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 204 response (NO_CONTENT).
+ */
+ public static DefaultResponseCreator withNoContent() {
+ return new DefaultResponseCreator(HttpStatus.NO_CONTENT);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 400 response (BAD_REQUEST).
+ */
+ public static DefaultResponseCreator withBadRequest() {
+ return new DefaultResponseCreator(HttpStatus.BAD_REQUEST);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 401 response (UNAUTHORIZED).
+ */
+ public static DefaultResponseCreator withUnauthorizedRequest() {
+ return new DefaultResponseCreator(HttpStatus.UNAUTHORIZED);
+ }
+
+ /**
+ * {@code ResponseCreator} for a 500 response (SERVER_ERROR).
+ */
+ public static DefaultResponseCreator withServerError() {
+ return new DefaultResponseCreator(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+ /**
+ * {@code ResponseCreator} with a specific HTTP status.
+ * @param status the response status
+ */
+ public static DefaultResponseCreator withStatus(HttpStatus status) {
+ return new DefaultResponseCreator(status);
+ }
+
+ /**
+ * Respond with a given body, headers, status code, and status text.
+ *
+ * @param body the body of the response "UTF-8" encoded
+ * @param headers the response headers
+ * @param statusCode the response status code
+ * @param statusText the response status text
+ *
+ * @deprecated in favor of methods returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(final String body, final HttpHeaders headers,
+ final HttpStatus statusCode, final String statusText) {
+
+ return new ResponseCreator() {
+ public MockClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
+ MockClientHttpResponse response = new MockClientHttpResponse(body.getBytes("UTF-8"), statusCode);
+ response.getHeaders().putAll(headers);
+ return response;
+ }
+ };
+ }
+
+ /**
+ * Respond with the given body, headers, and a status code of 200 (OK).
+ *
+ * @param body the body of the response "UTF-8" encoded
+ * @param headers the response headers
+ *
+ * @deprecated in favor of methods 'withXyz' in this class returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(String body, HttpHeaders headers) {
+ return withResponse(body, headers, HttpStatus.OK, "");
+ }
+
+ /**
+ * Respond with a given body, headers, status code, and text.
+ *
+ * @param body a {@link Resource} containing the body of the response
+ * @param headers the response headers
+ * @param statusCode the response status code
+ * @param statusText the response status text
+ *
+ * @deprecated in favor of methods 'withXyz' in this class returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(final Resource body, final HttpHeaders headers,
+ final HttpStatus statusCode, String statusText) {
+
+ return new ResponseCreator() {
+ public MockClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
+ MockClientHttpResponse response = new MockClientHttpResponse(body.getInputStream(), statusCode);
+ response.getHeaders().putAll(headers);
+ return response;
+ }
+ };
+ }
+
+ /**
+ * Respond with the given body, headers, and a status code of 200 (OK).
+ * @param body the body of the response
+ * @param headers the response headers
+ *
+ * @deprecated in favor of methods 'withXyz' in this class returning DefaultResponseCreator
+ */
+ public static ResponseCreator withResponse(final Resource body, final HttpHeaders headers) {
+ return withResponse(body, headers, HttpStatus.OK, "");
+ }
+
+}
View
23 ...est-mvc/src/main/java/org/springframework/test/web/mock/client/response/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2002-2012 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.
+ */
+
+/**
+ * Contains built-in {@link org.springframework.test.web.mock.client.ResponseCreator}
+ * implementations. Use
+ * {@link org.springframework.test.web.mock.client.response.ResponseCreators}
+ * to gain access to instances of those implementations.
+ */
+package org.springframework.test.web.mock.client.response;
View
99 ...ng-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/DefaultMvcResult.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2002-2012 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.mock.servlet;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.web.servlet.FlashMap;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.support.RequestContextUtils;
+
+/**
+ * A simple implementation of {@link MvcResult} with setters.
+ *
+ * @author Rossen Stoyanchev
+ * @author Rob Winch
+ * @since 3.2
+ */
+class DefaultMvcResult implements MvcResult {
+
+ private final MockHttpServletRequest mockRequest;
+
+ private final MockHttpServletResponse mockResponse;
+
+ private Object handler;
+
+ private HandlerInterceptor[] interceptors;
+
+ private ModelAndView modelAndView;
+
+ private Exception resolvedException;
+
+
+ /**
+ * Create a new instance with the given request and response.
+ */
+ public DefaultMvcResult(MockHttpServletRequest request, MockHttpServletResponse response) {
+ this.mockRequest = request;
+ this.mockResponse = response;
+ }
+
+ public MockHttpServletResponse getResponse() {
+ return mockResponse;
+ }
+
+ public MockHttpServletRequest getRequest() {
+ return mockRequest;
+ }
+
+ public Object getHandler() {
+ return this.handler;
+ }
+
+ public void setHandler(Object handler) {
+ this.handler = handler;
+ }
+
+ public HandlerInterceptor[] getInterceptors() {
+ return this.interceptors;
+ }
+
+ public void setInterceptors(HandlerInterceptor[] interceptors) {
+ this.interceptors = interceptors;
+ }
+
+ public Exception getResolvedException() {
+ return this.resolvedException;
+ }
+
+ public void setResolvedException(Exception resolvedException) {
+ this.resolvedException = resolvedException;
+ }
+
+ public ModelAndView getModelAndView() {
+ return this.modelAndView;
+ }
+
+ public void setModelAndView(ModelAndView mav) {
+ this.modelAndView = mav;
+ }
+
+ public FlashMap getFlashMap() {
+ return RequestContextUtils.getOutputFlashMap(mockRequest);
+ }
+
+}
View
167 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/MockMvc.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2002-2012 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.mock.servlet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.springframework.beans.Mergeable;
+import org.springframework.mock.web.MockFilterChain;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.util.Assert;
+
+/**
+ * <strong>Main entry point for server-side Spring MVC test support.</strong>
+ *
+ * <p>Below is an example:
+ *
+ * <pre>
+ * static imports:
+ * MockMvcBuilders.*, MockMvcRequestBuilders.*, MockMvcResultMatchers.*
+ *
+ * WebApplicationContext wac = ...;
+ *
+ * MockMvc mockMvc = webAppContextSetup(wac).configureWarRootDir("src/main/webapp", false).build()
+ *
+ * mockMvc.perform(get("/form"))
+ * .andExpect(status().isOk())
+ * .andExpect(content().mimeType("text/html"))
+ * .andExpect(forwardedUrl("/WEB-INF/layouts/main.jsp"));
+ * </pre>
+ *
+ * @author Rossen Stoyanchev
+ * @author Rob Winch
+ * @since 3.2
+ */
+public final class MockMvc {
+
+ static String MVC_RESULT_ATTRIBUTE = MockMvc.class.getName().concat(".MVC_RESULT_ATTRIBUTE");
+
+ private final MockFilterChain filterChain;
+
+ private final ServletContext servletContext;
+
+ private RequestBuilder defaultRequestBuilder;
+
+ private List<ResultMatcher> defaultResultMatchers = new ArrayList<ResultMatcher>();
+
+ private List<ResultHandler> defaultResultHandlers = new ArrayList<ResultHandler>();
+
+
+ /**
+ * Private constructor, not for direct instantiation.
+ * @see org.springframework.test.web.mock.servlet.setup.MockMvcBuilders
+ */
+ MockMvc(MockFilterChain filterChain, ServletContext servletContext) {
+ Assert.notNull(servletContext, "A ServletContext is required");
+ Assert.notNull(filterChain, "A MockFilterChain is required");
+
+ this.filterChain = filterChain;
+ this.servletContext = servletContext;
+ }
+
+ /**
+ * A default request builder merged into every performed request.
+ * @see org.springframework.test.web.mock.servlet.setup.DefaultMockMvcBuilder#defaultRequest(RequestBuilder)
+ */
+ void setDefaultRequest(RequestBuilder requestBuilder) {
+ this.defaultRequestBuilder = requestBuilder;
+ }
+
+ /**
+ * Expectations to assert after every performed request.
+ * @see org.springframework.test.web.mock.servlet.setup.DefaultMockMvcBuilder#alwaysExpect(ResultMatcher)
+ */
+ void setGlobalResultMatchers(List<ResultMatcher> resultMatchers) {
+ Assert.notNull(resultMatchers, "resultMatchers is required");
+ this.defaultResultMatchers = resultMatchers;
+ }
+
+ /**
+ * General actions to apply after every performed request.
+ * @see org.springframework.test.web.mock.servlet.setup.DefaultMockMvcBuilder#alwaysDo(ResultHandler)
+ */
+ void setGlobalResultHandlers(List<ResultHandler> resultHandlers) {
+ Assert.notNull(resultHandlers, "resultHandlers is required");
+ this.defaultResultHandlers = resultHandlers;
+ }
+
+ /**
+ * Perform a request and return a type that allows chaining further
+ * actions, such as asserting expectations, on the result.
+ *
+ * @param requestBuilder used to prepare the request to execute;
+ * see static factory methods in
+ * {@link org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders}
+ *
+ * @return an instance of {@link ResultActions}; never {@code null}
+ *
+ * @see org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders
+ * @see org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers
+ */
+ public ResultActions perform(RequestBuilder requestBuilder) throws Exception {
+
+ if (this.defaultRequestBuilder != null) {
+ if (requestBuilder instanceof Mergeable) {
+ requestBuilder = (RequestBuilder) ((Mergeable) requestBuilder).merge(this.defaultRequestBuilder);
+ }
+ }
+
+ MockHttpServletRequest request = requestBuilder.buildRequest(this.servletContext);
+ MockHttpServletResponse response = new MockHttpServletResponse();
+
+ final MvcResult mvcResult = new DefaultMvcResult(request, response);
+ request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult);
+
+ this.filterChain.reset();
+ this.filterChain.doFilter(request, response);
+
+ applyDefaultResultActions(mvcResult);
+
+ return new ResultActions() {
+
+ public ResultActions andExpect(ResultMatcher matcher) throws Exception {
+ matcher.match(mvcResult);
+ return this;
+ }
+
+ public ResultActions andDo(ResultHandler printer) throws Exception {
+ printer.handle(mvcResult);
+ return this;
+ }
+
+ public MvcResult andReturn() {
+ return mvcResult;
+ }
+ };
+ }
+
+ private void applyDefaultResultActions(MvcResult mvcResult) throws Exception {
+
+ for (ResultMatcher matcher : this.defaultResultMatchers) {
+ matcher.match(mvcResult);
+ }
+
+ for (ResultHandler handler : this.defaultResultHandlers) {
+ handler.handle(mvcResult);
+ }
+ }
+
+}
View
35 spring-test-mvc/src/main/java/org/springframework/test/web/mock/servlet/MockMvcBuilder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2012 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.mock.servlet;
+
+/**
+ * Builds a {@link MockMvc}.
+ *
+ * <p>See static, factory methods in
+ * {@code org.springframework.test.web.server.setup.MockMvcBuilders}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public interface MockMvcBuilder {
+
+ /**
+ * Build a {@link MockMvc} instance.
+ */
+ MockMvc build();
+
+}
View
77 ...st-mvc/src/main/java/org/springframework/test/web/mock/servlet/MockMvcBuilderSupport.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2002-2012 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.mock.servlet;
+
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.springframework.core.NestedRuntimeException;
+import org.springframework.mock.web.MockFilterChain;
+import org.springframework.mock.web.MockServletConfig;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * Base class for MockMvc builder implementations, providing the capability to
+ * create a {@link MockMvc} instance.
+ *
+ * <p>{@link org.springframework.test.web.mock.servlet.setup.DefaultMockMvcBuilder},
+ * which derives from this class, provides a