summaryrefslogtreecommitdiff
path: root/spring-test
diff options
context:
space:
mode:
authorEmmanuel Bourg <ebourg@apache.org>2016-08-03 19:55:01 +0200
committerEmmanuel Bourg <ebourg@apache.org>2016-08-03 19:55:01 +0200
commit75a721d1019da2a2fa86e24ff439df4a224e5b19 (patch)
tree2c44c00ce2c8641cccad177177e5682e187a17ea /spring-test
parent9eaca6a06af3cbceb3754de19d477be770614265 (diff)
Imported Upstream version 4.3.2
Diffstat (limited to 'spring-test')
-rw-r--r--spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java4
-rw-r--r--spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java4
-rw-r--r--spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java11
-rw-r--r--spring-test/src/main/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java5
-rw-r--r--spring-test/src/main/java/org/springframework/test/annotation/Commit.java4
-rw-r--r--spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java10
-rw-r--r--spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java48
-rw-r--r--spring-test/src/main/java/org/springframework/test/annotation/Rollback.java2
-rw-r--r--spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java5
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java4
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java71
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java6
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java6
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java30
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java49
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java52
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java6
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java64
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java15
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/TestContextManager.java76
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java2
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java4
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java4
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java24
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/cache/ContextCacheUtils.java54
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/cache/DefaultContextCache.java93
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java4
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java9
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java10
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java9
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java27
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/SpringRunner.java52
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/package-info.java3
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java14
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java14
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java24
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java8
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java8
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java24
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java17
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java7
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java132
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java6
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java18
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java127
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java3
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java14
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java14
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java41
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java7
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java166
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java9
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java52
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java3
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java44
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java22
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java135
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizer.java52
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizerFactory.java73
-rw-r--r--spring-test/src/main/java/org/springframework/test/context/web/socket/package-info.java4
-rw-r--r--spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java7
-rw-r--r--spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java3
-rw-r--r--spring-test/src/main/java/org/springframework/test/util/AssertionErrors.java14
-rw-r--r--spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java39
-rw-r--r--spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java43
-rw-r--r--spring-test/src/main/java/org/springframework/test/util/ReflectionTestUtils.java71
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java189
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/DefaultRequestExpectation.java146
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/ExpectedCount.java118
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java8
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java308
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java42
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/RequestExpectationManager.java63
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/RequestMatcher.java3
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java81
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java82
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java58
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java43
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java2
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java17
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java3
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java2
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java109
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java48
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java30
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java2
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java8
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java119
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java69
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/result/HandlerResultMatchers.java91
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java88
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java62
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultMatchers.java2
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java9
-rw-r--r--spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java4
-rw-r--r--spring-test/src/main/resources/META-INF/spring.factories5
-rw-r--r--spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java28
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java24
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java34
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java13
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTestNGTests.java36
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java17
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheTests.java3
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheUtilsTests.java89
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/cache/LruContextCacheTests.java177
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/cache/SpringRunnerContextCacheTests.java3
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java65
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java27
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java43
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java47
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java45
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java39
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java58
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java31
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java95
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java27
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java46
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java32
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java57
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java27
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java44
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java37
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesOverridePropertiesFilesTestPropertySourceTests.java69
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/jdbc/ComposedAnnotationSqlScriptsTests.java72
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationSqlScriptsTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java89
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/AbstractTransactionalSpringRunnerTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java11
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelDisabledSpringRunnerTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelTransactionalSpringRunnerTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/ContextCustomizerSpringRunnerTests.java66
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackFalseRollbackAnnotationTransactionalTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackTrueRollbackAnnotationTransactionalTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/EnabledAndIgnoredSpringRunnerTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java10
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/HardCodedProfileValueSourceSpringRunnerTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/OptionalContextConfigurationSpringRunnerTests.java58
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit47ClassRunnerRuleTests.java8
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/StandardJUnit4FeaturesSpringRunnerTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/TimedTransactionalSpringRunnerTests.java4
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/BarConfig.java34
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/FooConfig.java34
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/InitializerConfiguredViaMetaAnnotationTests.java92
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsContextInitializerTests.java66
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java33
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java10
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java13
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java145
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/testng/AnnotationConfigTransactionalTestNGSpringContextTests.java30
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/testng/ConcreteTransactionalTestNGSpringContextTests.java30
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/testng/web/TestNGSpringContextWebTests.java24
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java122
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java166
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerTests.java110
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/web/WebAppConfigurationBootstrapWithTests.java78
-rw-r--r--spring-test/src/test/java/org/springframework/test/context/web/socket/WebSocketServletServerContainerFactoryBeanTests.java67
-rw-r--r--spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java119
-rw-r--r--spring-test/src/test/java/org/springframework/test/util/ReflectionTestUtilsTests.java105
-rw-r--r--spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntity.java34
-rw-r--r--spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntityException.java32
-rw-r--r--spring-test/src/test/java/org/springframework/test/util/subpackage/PersistentEntity.java7
-rw-r--r--spring-test/src/test/java/org/springframework/test/util/subpackage/Person.java84
-rw-r--r--spring-test/src/test/java/org/springframework/test/util/subpackage/PersonEntity.java96
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/DefaultRequestExpectationTests.java99
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/MockClientHttpRequestFactoryTests.java102
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java110
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java173
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/UnorderedRequestExpectationManagerTests.java133
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java36
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/match/MockRestRequestMatchersTests.java3
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/samples/SampleAsyncTests.java39
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/samples/SampleTests.java38
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatchersIntegrationTests.java1
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java3
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java32
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java85
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java97
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java3
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java28
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java43
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java41
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/result/JsonPathResultMatchersTests.java44
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/result/StatusResultMatchersTests.java6
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/AsyncControllerJavaConfigTests.java8
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/PersonController.java15
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java70
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ViewResolutionTests.java11
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java10
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HandlerAssertionTests.java74
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java142
-rw-r--r--spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java2
-rw-r--r--spring-test/src/test/resources/log4j.properties3
203 files changed, 6595 insertions, 1813 deletions
diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java
index 01840d66..d1a8c37c 100644
--- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java
+++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java
@@ -390,8 +390,8 @@ public class MockHttpServletRequest implements HttpServletRequest {
if (contentType != null) {
try {
MediaType mediaType = MediaType.parseMediaType(contentType);
- if (mediaType.getCharSet() != null) {
- this.characterEncoding = mediaType.getCharSet().name();
+ if (mediaType.getCharset() != null) {
+ this.characterEncoding = mediaType.getCharset().name();
}
}
catch (Exception ex) {
diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java
index 5fc724b8..1d5b838d 100644
--- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java
+++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java
@@ -236,8 +236,8 @@ public class MockHttpServletResponse implements HttpServletResponse {
if (contentType != null) {
try {
MediaType mediaType = MediaType.parseMediaType(contentType);
- if (mediaType.getCharSet() != null) {
- this.characterEncoding = mediaType.getCharSet().name();
+ if (mediaType.getCharset() != null) {
+ this.characterEncoding = mediaType.getCharset().name();
this.charset = true;
}
}
diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java
index 8be8a9a1..7bca348a 100644
--- a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java
+++ b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -25,7 +25,6 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -144,7 +143,7 @@ public class MockServletContext implements ServletContext {
private String servletContextName = "MockServletContext";
- private final Set<String> declaredRoles = new HashSet<String>();
+ private final Set<String> declaredRoles = new LinkedHashSet<String>();
private Set<SessionTrackingMode> sessionTrackingModes;
@@ -370,7 +369,6 @@ public class MockServletContext implements ServletContext {
/**
* Register a {@link RequestDispatcher} (typically a {@link MockRequestDispatcher})
* that acts as a wrapper for the named Servlet.
- *
* @param name the name of the wrapped Servlet
* @param requestDispatcher the dispatcher that wraps the named Servlet
* @see #getNamedDispatcher
@@ -384,7 +382,6 @@ public class MockServletContext implements ServletContext {
/**
* Unregister the {@link RequestDispatcher} with the given name.
- *
* @param name the name of the dispatcher to unregister
* @see #getNamedDispatcher
* @see #registerNamedDispatcher
@@ -429,13 +426,13 @@ public class MockServletContext implements ServletContext {
@Override
@Deprecated
public Enumeration<Servlet> getServlets() {
- return Collections.enumeration(new HashSet<Servlet>());
+ return Collections.enumeration(Collections.<Servlet>emptySet());
}
@Override
@Deprecated
public Enumeration<String> getServletNames() {
- return Collections.enumeration(new HashSet<String>());
+ return Collections.enumeration(Collections.<String>emptySet());
}
@Override
diff --git a/spring-test/src/main/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java b/spring-test/src/main/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java
index 66a663e0..716f71b0 100644
--- a/spring-test/src/main/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java
+++ b/spring-test/src/main/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -21,7 +21,6 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
-import java.util.HashSet;
import java.util.Set;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequestDispatcher;
@@ -156,7 +155,7 @@ public class ServletWrappingPortletContext implements PortletContext {
@Override
public Enumeration<String> getContainerRuntimeOptions() {
- return Collections.enumeration(new HashSet<String>());
+ return Collections.enumeration(Collections.<String>emptySet());
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Commit.java b/spring-test/src/main/java/org/springframework/test/annotation/Commit.java
index e23c254f..98b3d96b 100644
--- a/spring-test/src/main/java/org/springframework/test/annotation/Commit.java
+++ b/spring-test/src/main/java/org/springframework/test/annotation/Commit.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -18,6 +18,7 @@ package org.springframework.test.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -51,6 +52,7 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
+@Inherited
@Rollback(false)
public @interface Commit {
}
diff --git a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java
index cd285587..e5f61e45 100644
--- a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -24,7 +24,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * <p>{@code ProfileValueSourceConfiguration} is a class-level annotation which
+ * {@code ProfileValueSourceConfiguration} is a class-level annotation which
* is used to specify what type of {@link ProfileValueSource} to use when
* retrieving <em>profile values</em> configured via the {@link IfProfileValue
* &#064;IfProfileValue} annotation.
@@ -38,17 +38,15 @@ import java.lang.annotation.Target;
* @see IfProfileValue
* @see ProfileValueUtils
*/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
public @interface ProfileValueSourceConfiguration {
/**
- * <p>
* The type of {@link ProfileValueSource} to use when retrieving
* <em>profile values</em>.
- * </p>
*
* @see SystemProfileValueSource
*/
diff --git a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java
index 27ce7f64..17c60cad 100644
--- a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -21,13 +21,12 @@ import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
-import static org.springframework.core.annotation.AnnotationUtils.*;
-
/**
* General utility methods for working with <em>profile values</em>.
*
@@ -49,12 +48,10 @@ public abstract class ProfileValueUtils {
* {@link ProfileValueSourceConfiguration
* &#064;ProfileValueSourceConfiguration} annotation and instantiates a new
* instance of that type.
- * <p>
- * If {@link ProfileValueSourceConfiguration
+ * <p>If {@link ProfileValueSourceConfiguration
* &#064;ProfileValueSourceConfiguration} is not present on the specified
* class or if a custom {@link ProfileValueSource} is not declared, the
* default {@link SystemProfileValueSource} will be returned instead.
- *
* @param testClass The test class for which the ProfileValueSource should
* be retrieved
* @return the configured (or default) ProfileValueSource for the specified
@@ -66,10 +63,10 @@ public abstract class ProfileValueUtils {
Assert.notNull(testClass, "testClass must not be null");
Class<ProfileValueSourceConfiguration> annotationType = ProfileValueSourceConfiguration.class;
- ProfileValueSourceConfiguration config = findAnnotation(testClass, annotationType);
+ ProfileValueSourceConfiguration config = AnnotatedElementUtils.findMergedAnnotation(testClass, annotationType);
if (logger.isDebugEnabled()) {
- logger.debug("Retrieved @ProfileValueSourceConfiguration [" + config + "] for test class ["
- + testClass.getName() + "]");
+ logger.debug("Retrieved @ProfileValueSourceConfiguration [" + config + "] for test class [" +
+ testClass.getName() + "]");
}
Class<? extends ProfileValueSource> profileValueSourceType;
@@ -80,8 +77,8 @@ public abstract class ProfileValueUtils {
profileValueSourceType = (Class<? extends ProfileValueSource>) AnnotationUtils.getDefaultValue(annotationType);
}
if (logger.isDebugEnabled()) {
- logger.debug("Retrieved ProfileValueSource type [" + profileValueSourceType + "] for class ["
- + testClass.getName() + "]");
+ logger.debug("Retrieved ProfileValueSource type [" + profileValueSourceType + "] for class [" +
+ testClass.getName() + "]");
}
ProfileValueSource profileValueSource;
@@ -92,10 +89,10 @@ public abstract class ProfileValueUtils {
try {
profileValueSource = profileValueSourceType.newInstance();
}
- catch (Exception e) {
+ catch (Exception ex) {
if (logger.isWarnEnabled()) {
- logger.warn("Could not instantiate a ProfileValueSource of type [" + profileValueSourceType
- + "] for class [" + testClass.getName() + "]: using default.", e);
+ logger.warn("Could not instantiate a ProfileValueSource of type [" + profileValueSourceType +
+ "] for class [" + testClass.getName() + "]: using default.", ex);
}
profileValueSource = SystemProfileValueSource.getInstance();
}
@@ -108,16 +105,14 @@ public abstract class ProfileValueUtils {
* Determine if the supplied {@code testClass} is <em>enabled</em> in
* the current environment, as specified by the {@link IfProfileValue
* &#064;IfProfileValue} annotation at the class level.
- * <p>
- * Defaults to {@code true} if no {@link IfProfileValue
+ * <p>Defaults to {@code true} if no {@link IfProfileValue
* &#064;IfProfileValue} annotation is declared.
- *
* @param testClass the test class
* @return {@code true} if the test is <em>enabled</em> in the current
* environment
*/
public static boolean isTestEnabledInThisEnvironment(Class<?> testClass) {
- IfProfileValue ifProfileValue = findAnnotation(testClass, IfProfileValue.class);
+ IfProfileValue ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testClass, IfProfileValue.class);
return isTestEnabledInThisEnvironment(retrieveProfileValueSource(testClass), ifProfileValue);
}
@@ -127,10 +122,8 @@ public abstract class ProfileValueUtils {
* &#064;IfProfileValue} annotation, which may be declared on the test
* method itself or at the class level. Class-level usage overrides
* method-level usage.
- * <p>
- * Defaults to {@code true} if no {@link IfProfileValue
+ * <p>Defaults to {@code true} if no {@link IfProfileValue
* &#064;IfProfileValue} annotation is declared.
- *
* @param testMethod the test method
* @param testClass the test class
* @return {@code true} if the test is <em>enabled</em> in the current
@@ -146,10 +139,8 @@ public abstract class ProfileValueUtils {
* &#064;IfProfileValue} annotation, which may be declared on the test
* method itself or at the class level. Class-level usage overrides
* method-level usage.
- * <p>
- * Defaults to {@code true} if no {@link IfProfileValue
+ * <p>Defaults to {@code true} if no {@link IfProfileValue
* &#064;IfProfileValue} annotation is declared.
- *
* @param profileValueSource the ProfileValueSource to use to determine if
* the test is enabled
* @param testMethod the test method
@@ -160,11 +151,11 @@ public abstract class ProfileValueUtils {
public static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource, Method testMethod,
Class<?> testClass) {
- IfProfileValue ifProfileValue = findAnnotation(testClass, IfProfileValue.class);
+ IfProfileValue ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testClass, IfProfileValue.class);
boolean classLevelEnabled = isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue);
if (classLevelEnabled) {
- ifProfileValue = findAnnotation(testMethod, IfProfileValue.class);
+ ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testMethod, IfProfileValue.class);
return isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue);
}
@@ -175,7 +166,6 @@ public abstract class ProfileValueUtils {
* Determine if the {@code value} (or one of the {@code values})
* in the supplied {@link IfProfileValue &#064;IfProfileValue} annotation is
* <em>enabled</em> in the current environment.
- *
* @param profileValueSource the ProfileValueSource to use to determine if
* the test is enabled
* @param ifProfileValue the annotation to introspect; may be
@@ -195,8 +185,8 @@ public abstract class ProfileValueUtils {
String[] annotatedValues = ifProfileValue.values();
if (StringUtils.hasLength(ifProfileValue.value())) {
if (annotatedValues.length > 0) {
- throw new IllegalArgumentException("Setting both the 'value' and 'values' attributes "
- + "of @IfProfileValue is not allowed: choose one or the other.");
+ throw new IllegalArgumentException("Setting both the 'value' and 'values' attributes " +
+ "of @IfProfileValue is not allowed: choose one or the other.");
}
annotatedValues = new String[] { ifProfileValue.value() };
}
diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java b/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java
index 37bf2102..c1f06e64 100644
--- a/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java
+++ b/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java
@@ -18,6 +18,7 @@ package org.springframework.test.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -56,6 +57,7 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
+@Inherited
public @interface Rollback {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java
index 5f4eb1c2..a2b33faa 100644
--- a/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/annotation/TestAnnotationUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -19,7 +19,6 @@ package org.springframework.test.annotation;
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotatedElementUtils;
-import org.springframework.core.annotation.AnnotationUtils;
/**
* Collection of utility methods for working with Spring's core testing annotations.
@@ -52,7 +51,7 @@ public class TestAnnotationUtils {
* not annotated with {@code @Repeat}
*/
public static int getRepeatCount(Method method) {
- Repeat repeat = AnnotationUtils.findAnnotation(method, Repeat.class);
+ Repeat repeat = AnnotatedElementUtils.findMergedAnnotation(method, Repeat.class);
if (repeat == null) {
return 1;
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
index fc259102..54b08892 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
@@ -43,10 +43,10 @@ import org.springframework.core.annotation.AliasFor;
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.annotation.Profile
*/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
public @interface ActiveProfiles {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java
index c16d7def..22358071 100644
--- a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java
@@ -17,22 +17,22 @@
package org.springframework.test.context;
import java.lang.reflect.Constructor;
-import java.util.List;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
-import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.ClassUtils;
-import org.springframework.util.MultiValueMap;
/**
* {@code BootstrapUtils} is a collection of utility methods to assist with
* bootstrapping the <em>Spring TestContext Framework</em>.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 4.1
* @see BootstrapWith
* @see BootstrapContext
@@ -49,6 +49,12 @@ abstract class BootstrapUtils {
private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME =
"org.springframework.test.context.support.DefaultTestContextBootstrapper";
+ private static final String DEFAULT_WEB_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME =
+ "org.springframework.test.context.web.WebTestContextBootstrapper";
+
+ private static final String WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME =
+ "org.springframework.test.context.web.WebAppConfiguration";
+
private static final Log logger = LogFactory.getLog(BootstrapUtils.class);
@@ -103,51 +109,64 @@ abstract class BootstrapUtils {
* <p>If the {@link BootstrapWith @BootstrapWith} annotation is present on
* the test class, either directly or as a meta-annotation, then its
* {@link BootstrapWith#value value} will be used as the bootstrapper type.
- * Otherwise, the {@link org.springframework.test.context.support.DefaultTestContextBootstrapper
- * DefaultTestContextBootstrapper} will be used.
+ * Otherwise, either the
+ * {@link org.springframework.test.context.support.DefaultTestContextBootstrapper
+ * DefaultTestContextBootstrapper} or the
+ * {@link org.springframework.test.context.web.WebTestContextBootstrapper
+ * WebTestContextBootstrapper} will be used, depending on the presence of
+ * {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}.
* @param bootstrapContext the bootstrap context to use
* @return a fully configured {@code TestContextBootstrapper}
*/
- @SuppressWarnings("unchecked")
static TestContextBootstrapper resolveTestContextBootstrapper(BootstrapContext bootstrapContext) {
Class<?> testClass = bootstrapContext.getTestClass();
- Class<? extends TestContextBootstrapper> clazz = null;
+ Class<?> clazz = null;
try {
- MultiValueMap<String, Object> attributesMultiMap = AnnotatedElementUtils.getAllAnnotationAttributes(
- testClass, BootstrapWith.class.getName());
- List<Object> values = (attributesMultiMap == null ? null : attributesMultiMap.get(AnnotationUtils.VALUE));
-
- if (values != null) {
- if (values.size() != 1) {
- throw new IllegalStateException(String.format("Configuration error: found multiple declarations of " +
- "@BootstrapWith on test class [%s] with values %s", testClass.getName(), values));
- }
- clazz = (Class<? extends TestContextBootstrapper>) values.get(0);
- }
- else {
- clazz = (Class<? extends TestContextBootstrapper>) ClassUtils.forName(
- DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, BootstrapUtils.class.getClassLoader());
+ clazz = resolveExplicitTestContextBootstrapper(testClass);
+ if (clazz == null) {
+ clazz = resolveDefaultTestContextBootstrapper(testClass);
}
-
if (logger.isDebugEnabled()) {
logger.debug(String.format("Instantiating TestContextBootstrapper for test class [%s] from class [%s]",
testClass.getName(), clazz.getName()));
}
-
TestContextBootstrapper testContextBootstrapper =
BeanUtils.instantiateClass(clazz, TestContextBootstrapper.class);
testContextBootstrapper.setBootstrapContext(bootstrapContext);
return testContextBootstrapper;
}
+ catch (IllegalStateException ex) {
+ throw ex;
+ }
catch (Throwable ex) {
- if (ex instanceof IllegalStateException) {
- throw (IllegalStateException) ex;
- }
throw new IllegalStateException("Could not load TestContextBootstrapper [" + clazz +
"]. Specify @BootstrapWith's 'value' attribute or make the default bootstrapper class available.",
ex);
}
}
+ private static Class<?> resolveExplicitTestContextBootstrapper(Class<?> testClass) {
+ Set<BootstrapWith> annotations = AnnotatedElementUtils.findAllMergedAnnotations(testClass, BootstrapWith.class);
+ if (annotations.size() < 1) {
+ return null;
+ }
+ if (annotations.size() > 1) {
+ throw new IllegalStateException(String.format(
+ "Configuration error: found multiple declarations of @BootstrapWith for test class [%s]: %s",
+ testClass.getName(), annotations));
+ }
+ return annotations.iterator().next().value();
+ }
+
+ private static Class<?> resolveDefaultTestContextBootstrapper(Class<?> testClass) throws Exception {
+ ClassLoader classLoader = BootstrapUtils.class.getClassLoader();
+ AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(testClass,
+ WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME, false, false);
+ if (attributes != null) {
+ return ClassUtils.forName(DEFAULT_WEB_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, classLoader);
+ }
+ return ClassUtils.forName(DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, classLoader);
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java b/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java
index 302ce42e..7cf742f6 100644
--- a/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java
+++ b/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -35,10 +35,10 @@ import java.lang.annotation.Target;
* @see BootstrapContext
* @see TestContextBootstrapper
*/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
public @interface BootstrapWith {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
index 9087bdbe..669adbd6 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -83,10 +83,10 @@ import org.springframework.core.annotation.AliasFor;
* @see MergedContextConfiguration
* @see org.springframework.context.ApplicationContext
*/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
public @interface ContextConfiguration {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java b/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java
index 0e0feb47..f32731df 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -34,6 +34,7 @@ import org.springframework.util.StringUtils;
* attributes declared via {@link ContextConfiguration @ContextConfiguration}.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 3.1
* @see ContextConfiguration
* @see SmartContextLoader#processContextConfiguration(ContextConfigurationAttributes)
@@ -41,6 +42,11 @@ import org.springframework.util.StringUtils;
*/
public class ContextConfigurationAttributes {
+ private static final String[] EMPTY_LOCATIONS = new String[0];
+
+ private static final Class<?>[] EMPTY_CLASSES = new Class<?>[0];
+
+
private static final Log logger = LogFactory.getLog(ContextConfigurationAttributes.class);
private final Class<?> declaringClass;
@@ -61,6 +67,17 @@ public class ContextConfigurationAttributes {
/**
+ * Construct a new {@link ContextConfigurationAttributes} instance with default values.
+ * @param declaringClass the test class that declared {@code @ContextConfiguration},
+ * either explicitly or implicitly
+ * @since 4.3
+ */
+ @SuppressWarnings("unchecked")
+ public ContextConfigurationAttributes(Class<?> declaringClass) {
+ this(declaringClass, EMPTY_LOCATIONS, EMPTY_CLASSES, false, (Class[]) EMPTY_CLASSES, true, ContextLoader.class);
+ }
+
+ /**
* Construct a new {@link ContextConfigurationAttributes} instance for the
* supplied {@link ContextConfiguration @ContextConfiguration} annotation and
* the {@linkplain Class test class} that declared it.
@@ -84,8 +101,8 @@ public class ContextConfigurationAttributes {
@SuppressWarnings("unchecked")
public ContextConfigurationAttributes(Class<?> declaringClass, AnnotationAttributes annAttrs) {
this(declaringClass, annAttrs.getStringArray("locations"), annAttrs.getClassArray("classes"), annAttrs.getBoolean("inheritLocations"),
- (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[]) annAttrs.getClassArray("initializers"),
- annAttrs.getBoolean("inheritInitializers"), annAttrs.getString("name"), (Class<? extends ContextLoader>) annAttrs.getClass("loader"));
+ (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[]) annAttrs.getClassArray("initializers"),
+ annAttrs.getBoolean("inheritInitializers"), annAttrs.getString("name"), (Class<? extends ContextLoader>) annAttrs.getClass("loader"));
}
/**
@@ -139,8 +156,8 @@ public class ContextConfigurationAttributes {
if (!ObjectUtils.isEmpty(locations) && !ObjectUtils.isEmpty(classes) && logger.isDebugEnabled()) {
logger.debug(String.format(
"Test class [%s] has been configured with @ContextConfiguration's 'locations' (or 'value') %s " +
- "and 'classes' %s attributes. Most SmartContextLoader implementations support " +
- "only one declaration of resources per @ContextConfiguration annotation.",
+ "and 'classes' %s attributes. Most SmartContextLoader implementations support " +
+ "only one declaration of resources per @ContextConfiguration annotation.",
declaringClass.getName(), ObjectUtils.nullSafeToString(locations),
ObjectUtils.nullSafeToString(classes)));
}
@@ -158,7 +175,8 @@ public class ContextConfigurationAttributes {
/**
* Get the {@linkplain Class class} that declared the
- * {@link ContextConfiguration @ContextConfiguration} annotation.
+ * {@link ContextConfiguration @ContextConfiguration} annotation, either explicitly
+ * or implicitly.
* @return the declaring class (never {@code null})
*/
public Class<?> getDeclaringClass() {
diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java
new file mode 100644
index 00000000..d9462faa
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2016 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.context;
+
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * Strategy interface for customizing {@link ConfigurableApplicationContext
+ * application contexts} that are created and managed by the <em>Spring
+ * TestContext Framework</em>.
+ *
+ * <p>Customizers are created by {@link ContextCustomizerFactory} implementations.
+ *
+ * <p>Implementations must implement correct {@code equals} and {@code hashCode}
+ * methods since customizers form part of the {@link MergedContextConfiguration}
+ * which is used as a cache key.
+ *
+ * @author Phillip Webb
+ * @author Sam Brannen
+ * @since 4.3
+ * @see ContextCustomizerFactory
+ * @see org.springframework.test.context.support.AbstractContextLoader#customizeContext
+ */
+public interface ContextCustomizer {
+
+ /**
+ * Customize the supplied {@code ConfigurableApplicationContext} <em>after</em>
+ * bean definitions have been loaded into the context but <em>before</em> the
+ * context has been refreshed.
+ * @param context the context to customize
+ * @param mergedConfig the merged context configuration
+ */
+ void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig);
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java
new file mode 100644
index 00000000..9f07700a
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2016 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.context;
+
+import java.util.List;
+
+/**
+ * Factory for creating {@link ContextCustomizer ContextCustomizers}.
+ *
+ * <p>Factories are invoked after {@link ContextLoader ContextLoaders} have
+ * processed context configuration attributes but before the
+ * {@link MergedContextConfiguration} is created.
+ *
+ * <p>By default, the Spring TestContext Framework will use the
+ * {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
+ * mechanism for loading factories configured in all {@code META-INF/spring.factories}
+ * files on the classpath.
+ *
+ * @author Phillip Webb
+ * @author Sam Brannen
+ * @since 4.3
+ */
+public interface ContextCustomizerFactory {
+
+ /**
+ * Create a {@link ContextCustomizer} that should be used to customize a
+ * {@link org.springframework.context.ConfigurableApplicationContext ConfigurableApplicationContext}
+ * before it is refreshed.
+ * @param testClass the test class
+ * @param configAttributes the list of context configuration attributes for
+ * the test class, ordered <em>bottom-up</em> (i.e., as if we were traversing
+ * up the class hierarchy); never {@code null} or empty
+ * @return a {@link ContextCustomizer} or {@code null} if no customizer should
+ * be used
+ */
+ ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes);
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java b/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java
index 61b2fa8c..3a3d2859 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -139,10 +139,10 @@ import java.lang.annotation.Target;
* @see ContextConfiguration
* @see org.springframework.context.ApplicationContext
*/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
public @interface ContextHierarchy {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java
index 5090d7c5..45f31a18 100644
--- a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java
@@ -55,6 +55,7 @@ import org.springframework.util.StringUtils;
* that was loaded using properties of this {@code MergedContextConfiguration}.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 3.1
* @see ContextConfiguration
* @see ContextHierarchy
@@ -74,6 +75,8 @@ public class MergedContextConfiguration implements Serializable {
private static final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> EMPTY_INITIALIZER_CLASSES =
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
+ private static final Set<ContextCustomizer> EMPTY_CONTEXT_CUSTOMIZERS = Collections.<ContextCustomizer> emptySet();
+
private final Class<?> testClass;
@@ -89,6 +92,8 @@ public class MergedContextConfiguration implements Serializable {
private final String[] propertySourceProperties;
+ private final Set<ContextCustomizer> contextCustomizers;
+
private final ContextLoader contextLoader;
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
@@ -111,6 +116,11 @@ public class MergedContextConfiguration implements Serializable {
Collections.unmodifiableSet(contextInitializerClasses) : EMPTY_INITIALIZER_CLASSES);
}
+ private static Set<ContextCustomizer> processContextCustomizers(Set<ContextCustomizer> contextCustomizers) {
+ return (contextCustomizers != null ?
+ Collections.unmodifiableSet(contextCustomizers) : EMPTY_CONTEXT_CUSTOMIZERS);
+ }
+
private static String[] processActiveProfiles(String[] activeProfiles) {
if (activeProfiles == null) {
return EMPTY_STRING_ARRAY;
@@ -201,8 +211,8 @@ public class MergedContextConfiguration implements Serializable {
public MergedContextConfiguration(MergedContextConfiguration mergedConfig) {
this(mergedConfig.testClass, mergedConfig.locations, mergedConfig.classes,
mergedConfig.contextInitializerClasses, mergedConfig.activeProfiles, mergedConfig.propertySourceLocations,
- mergedConfig.propertySourceProperties, mergedConfig.contextLoader,
- mergedConfig.cacheAwareContextLoaderDelegate, mergedConfig.parent);
+ mergedConfig.propertySourceProperties, mergedConfig.contextCustomizers,
+ mergedConfig.contextLoader, mergedConfig.cacheAwareContextLoaderDelegate, mergedConfig.parent);
}
/**
@@ -233,6 +243,41 @@ public class MergedContextConfiguration implements Serializable {
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
MergedContextConfiguration parent) {
+ this(testClass, locations, classes, contextInitializerClasses, activeProfiles,
+ propertySourceLocations, propertySourceProperties,
+ EMPTY_CONTEXT_CUSTOMIZERS, contextLoader,
+ cacheAwareContextLoaderDelegate, parent);
+ }
+
+ /**
+ * Create a new {@code MergedContextConfiguration} instance for the
+ * supplied parameters.
+ * <p>If a {@code null} value is supplied for {@code locations},
+ * {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
+ * or {@code propertySourceProperties} an empty array will be stored instead.
+ * If a {@code null} value is supplied for {@code contextInitializerClasses}
+ * or {@code contextCustomizers}, an empty set will be stored instead.
+ * Furthermore, active profiles will be sorted, and duplicate profiles
+ * will be removed.
+ * @param testClass the test class for which the configuration was merged
+ * @param locations the merged context resource locations
+ * @param classes the merged annotated classes
+ * @param contextInitializerClasses the merged context initializer classes
+ * @param activeProfiles the merged active bean definition profiles
+ * @param propertySourceLocations the merged {@code PropertySource} locations
+ * @param propertySourceProperties the merged {@code PropertySource} properties
+ * @param contextCustomizers the context customizers
+ * @param contextLoader the resolved {@code ContextLoader}
+ * @param cacheAwareContextLoaderDelegate a cache-aware context loader
+ * delegate with which to retrieve the parent context
+ * @param parent the parent configuration or {@code null} if there is no parent
+ * @since 4.3
+ */
+ public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
+ Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
+ String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
+ Set<ContextCustomizer> contextCustomizers, ContextLoader contextLoader,
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
this.testClass = testClass;
this.locations = processStrings(locations);
@@ -241,6 +286,7 @@ public class MergedContextConfiguration implements Serializable {
this.activeProfiles = processActiveProfiles(activeProfiles);
this.propertySourceLocations = processStrings(propertySourceLocations);
this.propertySourceProperties = processStrings(propertySourceProperties);
+ this.contextCustomizers = processContextCustomizers(contextCustomizers);
this.contextLoader = contextLoader;
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
this.parent = parent;
@@ -349,6 +395,14 @@ public class MergedContextConfiguration implements Serializable {
}
/**
+ * Get the merged {@link ContextCustomizer ContextCustomizers} that will be applied
+ * when the application context is loaded.
+ */
+ public Set<ContextCustomizer> getContextCustomizers() {
+ return this.contextCustomizers;
+ }
+
+ /**
* Get the resolved {@link ContextLoader} for the {@linkplain #getTestClass() test class}.
*/
public ContextLoader getContextLoader() {
@@ -424,6 +478,9 @@ public class MergedContextConfiguration implements Serializable {
if (!Arrays.equals(this.propertySourceProperties, otherConfig.propertySourceProperties)) {
return false;
}
+ if (!this.contextCustomizers.equals(otherConfig.contextCustomizers)) {
+ return false;
+ }
if (this.parent == null) {
if (otherConfig.parent != null) {
@@ -454,6 +511,7 @@ public class MergedContextConfiguration implements Serializable {
result = 31 * result + Arrays.hashCode(this.activeProfiles);
result = 31 * result + Arrays.hashCode(this.propertySourceLocations);
result = 31 * result + Arrays.hashCode(this.propertySourceProperties);
+ result = 31 * result + this.contextCustomizers.hashCode();
result = 31 * result + (this.parent != null ? this.parent.hashCode() : 0);
result = 31 * result + nullSafeToString(this.contextLoader).hashCode();
return result;
@@ -466,6 +524,7 @@ public class MergedContextConfiguration implements Serializable {
* {@linkplain #getActiveProfiles() active profiles},
* {@linkplain #getPropertySourceLocations() property source locations},
* {@linkplain #getPropertySourceProperties() property source properties},
+ * {@linkplain #getContextCustomizers() context customizers},
* the name of the {@link #getContextLoader() ContextLoader}, and the
* {@linkplain #getParent() parent configuration}.
*/
@@ -479,6 +538,7 @@ public class MergedContextConfiguration implements Serializable {
.append("activeProfiles", ObjectUtils.nullSafeToString(this.activeProfiles))
.append("propertySourceLocations", ObjectUtils.nullSafeToString(this.propertySourceLocations))
.append("propertySourceProperties", ObjectUtils.nullSafeToString(this.propertySourceProperties))
+ .append("contextCustomizers", this.contextCustomizers)
.append("contextLoader", nullSafeToString(this.contextLoader))
.append("parent", this.parent)
.toString();
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java
index 2ec2b7d4..905ed70b 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -31,13 +31,14 @@ import java.util.List;
*
* <p>A custom bootstrapping strategy can be configured for a test class (or
* test class hierarchy) via {@link BootstrapWith @BootstrapWith}, either
- * directly or as a meta-annotation. See
- * {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
- * for an example.
+ * directly or as a meta-annotation.
*
- * <p>If a bootstrapper is not explicitly configured via {@code @BootstrapWith}, the
- * {@link org.springframework.test.context.support.DefaultTestContextBootstrapper DefaultTestContextBootstrapper}
- * will be used.
+ * <p>If a bootstrapper is not explicitly configured via {@code @BootstrapWith},
+ * either the {@link org.springframework.test.context.support.DefaultTestContextBootstrapper
+ * DefaultTestContextBootstrapper} or the
+ * {@link org.springframework.test.context.web.WebTestContextBootstrapper
+ * WebTestContextBootstrapper} will be used, depending on the presence of
+ * {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}.
*
* <h3>Implementation Notes</h3>
*
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java
index 3fd77678..28979647 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -25,6 +25,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
/**
* {@code TestContextManager} is the main entry point into the <em>Spring
@@ -37,18 +38,18 @@ import org.springframework.util.Assert;
*
* <ul>
* <li>{@link #beforeTestClass() before test class execution}: prior to any
- * <em>before class methods</em> of a particular testing framework (e.g., JUnit
- * 4's {@link org.junit.BeforeClass @BeforeClass})</li>
+ * <em>before class callbacks</em> of a particular testing framework (e.g.,
+ * JUnit 4's {@link org.junit.BeforeClass @BeforeClass})</li>
* <li>{@link #prepareTestInstance(Object) test instance preparation}:
* immediately following instantiation of the test instance</li>
* <li>{@link #beforeTestMethod(Object, Method) before test method execution}:
- * prior to any <em>before methods</em> of a particular testing framework (e.g.,
- * JUnit 4's {@link org.junit.Before @Before})</li>
+ * prior to any <em>before method callbacks</em> of a particular testing framework
+ * (e.g., JUnit 4's {@link org.junit.Before @Before})</li>
* <li>{@link #afterTestMethod(Object, Method, Throwable) after test method
- * execution}: after any <em>after methods</em> of a particular testing
+ * execution}: after any <em>after method callbacks</em> of a particular testing
* framework (e.g., JUnit 4's {@link org.junit.After @After})</li>
* <li>{@link #afterTestClass() after test class execution}: after any
- * <em>after class methods</em> of a particular testing framework (e.g., JUnit
+ * <em>after class callbacks</em> of a particular testing framework (e.g., JUnit
* 4's {@link org.junit.AfterClass @AfterClass})</li>
* </ul>
*
@@ -78,7 +79,6 @@ import org.springframework.util.Assert;
* @see TestExecutionListeners
* @see ContextConfiguration
* @see ContextHierarchy
- * @see org.springframework.test.context.transaction.TransactionConfiguration
*/
public class TestContextManager {
@@ -124,7 +124,7 @@ public class TestContextManager {
/**
* Get the {@link TestContext} managed by this {@code TestContextManager}.
*/
- protected final TestContext getTestContext() {
+ public final TestContext getTestContext() {
return this.testContext;
}
@@ -194,10 +194,12 @@ public class TestContextManager {
try {
testExecutionListener.beforeTestClass(getTestContext());
}
- catch (Exception ex) {
- logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
- "] to process 'before class' callback for test class [" + testClass + "]", ex);
- throw ex;
+ catch (Throwable ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
+ "] to process 'before class' callback for test class [" + testClass + "]", ex);
+ }
+ ReflectionUtils.rethrowException(ex);
}
}
}
@@ -227,10 +229,12 @@ public class TestContextManager {
try {
testExecutionListener.prepareTestInstance(getTestContext());
}
- catch (Exception ex) {
- logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
- "] to prepare test instance [" + testInstance + "]", ex);
- throw ex;
+ catch (Throwable ex) {
+ if (logger.isErrorEnabled()) {
+ logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
+ "] to prepare test instance [" + testInstance + "]", ex);
+ }
+ ReflectionUtils.rethrowException(ex);
}
}
}
@@ -264,11 +268,13 @@ public class TestContextManager {
try {
testExecutionListener.beforeTestMethod(getTestContext());
}
- catch (Exception ex) {
- logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
- "] to process 'before' execution of test method [" + testMethod + "] for test instance [" +
- testInstance + "]", ex);
- throw ex;
+ catch (Throwable ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
+ "] to process 'before' execution of test method [" + testMethod + "] for test instance [" +
+ testInstance + "]", ex);
+ }
+ ReflectionUtils.rethrowException(ex);
}
}
}
@@ -305,24 +311,26 @@ public class TestContextManager {
}
getTestContext().updateState(testInstance, testMethod, exception);
- Exception afterTestMethodException = null;
+ Throwable afterTestMethodException = null;
// Traverse the TestExecutionListeners in reverse order to ensure proper
// "wrapper"-style execution of listeners.
for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
try {
testExecutionListener.afterTestMethod(getTestContext());
}
- catch (Exception ex) {
- logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
- "] to process 'after' execution for test: method [" + testMethod + "], instance [" +
- testInstance + "], exception [" + exception + "]", ex);
+ catch (Throwable ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
+ "] to process 'after' execution for test: method [" + testMethod + "], instance [" +
+ testInstance + "], exception [" + exception + "]", ex);
+ }
if (afterTestMethodException == null) {
afterTestMethodException = ex;
}
}
}
if (afterTestMethodException != null) {
- throw afterTestMethodException;
+ ReflectionUtils.rethrowException(afterTestMethodException);
}
}
@@ -347,23 +355,25 @@ public class TestContextManager {
}
getTestContext().updateState(null, null, null);
- Exception afterTestClassException = null;
+ Throwable afterTestClassException = null;
// Traverse the TestExecutionListeners in reverse order to ensure proper
// "wrapper"-style execution of listeners.
for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
try {
testExecutionListener.afterTestClass(getTestContext());
}
- catch (Exception ex) {
- logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
- "] to process 'after class' callback for test class [" + testClass + "]", ex);
+ catch (Throwable ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener +
+ "] to process 'after class' callback for test class [" + testClass + "]", ex);
+ }
if (afterTestClassException == null) {
afterTestClassException = ex;
}
}
}
if (afterTestClassException != null) {
- throw afterTestClassException;
+ ReflectionUtils.rethrowException(afterTestClassException);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java
index 81d61cb2..fbca021a 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java
@@ -36,6 +36,8 @@ package org.springframework.test.context;
* <ul>
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener
* ServletTestExecutionListener}</li>
+ * <li>{@link org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener
+ * DirtiesContextBeforeModesTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* DependencyInjectionTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java
index d99c48d7..d1db5c75 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java
@@ -42,10 +42,10 @@ import org.springframework.core.annotation.AliasFor;
* @see TestContextManager
* @see ContextConfiguration
*/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
public @interface TestExecutionListeners {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java
index ec2528af..6937870b 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java
@@ -82,10 +82,10 @@ import org.springframework.core.annotation.AliasFor;
* @see org.springframework.core.env.PropertySource
* @see org.springframework.context.annotation.PropertySource
*/
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
public @interface TestPropertySource {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java b/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java
index 27902a6d..1f15c763 100644
--- a/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java
+++ b/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java
@@ -26,7 +26,9 @@ import org.springframework.test.context.MergedContextConfiguration;
* <em>Spring TestContext Framework</em>.
*
* <p>A {@code ContextCache} maintains a cache of {@code ApplicationContexts}
- * keyed by {@link MergedContextConfiguration} instances.
+ * keyed by {@link MergedContextConfiguration} instances, potentially configured
+ * with a {@linkplain ContextCacheUtils#retrieveMaxCacheSize maximum size} and
+ * a custom eviction policy.
*
* <h3>Rationale</h3>
* <p>Context caching can have significant performance benefits if context
@@ -40,6 +42,7 @@ import org.springframework.test.context.MergedContextConfiguration;
* @author Sam Brannen
* @author Juergen Hoeller
* @since 4.2
+ * @see ContextCacheUtils#retrieveMaxCacheSize()
*/
public interface ContextCache {
@@ -49,6 +52,25 @@ public interface ContextCache {
*/
String CONTEXT_CACHE_LOGGING_CATEGORY = "org.springframework.test.context.cache";
+ /**
+ * The default maximum size of the context cache: {@value #DEFAULT_MAX_CONTEXT_CACHE_SIZE}.
+ * @since 4.3
+ * @see #MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME
+ */
+ int DEFAULT_MAX_CONTEXT_CACHE_SIZE = 32;
+
+ /**
+ * System property used to configure the maximum size of the {@link ContextCache}
+ * as a positive integer. May alternatively be configured via the
+ * {@link org.springframework.core.SpringProperties} mechanism.
+ * <p>Note that implementations of {@code ContextCache} are not required to
+ * actually support a maximum cache size. Consult the documentation of the
+ * corresponding implementation for details.
+ * @since 4.3
+ * @see #DEFAULT_MAX_CONTEXT_CACHE_SIZE
+ */
+ String MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME = "spring.test.context.cache.maxSize";
+
/**
* Determine whether there is a cached context for the given key.
diff --git a/spring-test/src/main/java/org/springframework/test/context/cache/ContextCacheUtils.java b/spring-test/src/main/java/org/springframework/test/context/cache/ContextCacheUtils.java
new file mode 100644
index 00000000..66427cee
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/cache/ContextCacheUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2002-2016 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.context.cache;
+
+import org.springframework.core.SpringProperties;
+import org.springframework.util.StringUtils;
+
+/**
+ * Collection of utilities for working with {@link ContextCache ContextCaches}.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ */
+public abstract class ContextCacheUtils {
+
+ /**
+ * Retrieve the maximum size of the {@link ContextCache}.
+ * <p>Uses {@link SpringProperties} to retrieve a system property or Spring
+ * property named {@code spring.test.context.cache.maxSize}.
+ * <p>Falls back to the value of the {@link ContextCache#DEFAULT_MAX_CONTEXT_CACHE_SIZE}
+ * if no such property has been set or if the property is not an integer.
+ * @return the maximum size of the context cache
+ * @see ContextCache#MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME
+ */
+ public static int retrieveMaxCacheSize() {
+ try {
+ String maxSize = SpringProperties.getProperty(ContextCache.MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME);
+ if (StringUtils.hasText(maxSize)) {
+ return Integer.parseInt(maxSize.trim());
+ }
+ }
+ catch (Exception ex) {
+ // ignore
+ }
+
+ // Fallback
+ return ContextCache.DEFAULT_MAX_CONTEXT_CACHE_SIZE;
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/cache/DefaultContextCache.java b/spring-test/src/main/java/org/springframework/test/context/cache/DefaultContextCache.java
index 09678135..d4aaa441 100644
--- a/spring-test/src/main/java/org/springframework/test/context/cache/DefaultContextCache.java
+++ b/spring-test/src/main/java/org/springframework/test/context/cache/DefaultContextCache.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -17,7 +17,9 @@
package org.springframework.test.context.cache;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -37,12 +39,18 @@ import org.springframework.util.Assert;
/**
* Default implementation of the {@link ContextCache} API.
*
- * <p>Uses {@link ConcurrentHashMap ConcurrentHashMaps} to cache
- * {@link ApplicationContext} and {@link MergedContextConfiguration} instances.
+ * <p>Uses a synchronized {@link Map} configured with a maximum size
+ * and a <em>least recently used</em> (LRU) eviction policy to cache
+ * {@link ApplicationContext} instances.
+ *
+ * <p>The maximum size may be supplied as a {@linkplain #DefaultContextCache(int)
+ * constructor argument} or set via a system property or Spring property named
+ * {@code spring.test.context.cache.maxSize}.
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
+ * @see ContextCacheUtils#retrieveMaxCacheSize()
*/
public class DefaultContextCache implements ContextCache {
@@ -52,7 +60,7 @@ public class DefaultContextCache implements ContextCache {
* Map of context keys to Spring {@code ApplicationContext} instances.
*/
private final Map<MergedContextConfiguration, ApplicationContext> contextMap =
- new ConcurrentHashMap<MergedContextConfiguration, ApplicationContext>(64);
+ Collections.synchronizedMap(new LruCache(32, 0.75f));
/**
* Map of parent keys to sets of children keys, representing a top-down <em>tree</em>
@@ -61,7 +69,9 @@ public class DefaultContextCache implements ContextCache {
* of other contexts.
*/
private final Map<MergedContextConfiguration, Set<MergedContextConfiguration>> hierarchyMap =
- new ConcurrentHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(64);
+ new ConcurrentHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(32);
+
+ private final int maxSize;
private final AtomicInteger hitCount = new AtomicInteger();
@@ -69,6 +79,32 @@ public class DefaultContextCache implements ContextCache {
/**
+ * Create a new {@code DefaultContextCache} using the maximum cache size
+ * obtained via {@link ContextCacheUtils#retrieveMaxCacheSize()}.
+ * @since 4.3
+ * @see #DefaultContextCache(int)
+ * @see ContextCacheUtils#retrieveMaxCacheSize()
+ */
+ public DefaultContextCache() {
+ this(ContextCacheUtils.retrieveMaxCacheSize());
+ }
+
+ /**
+ * Create a new {@code DefaultContextCache} using the supplied maximum
+ * cache size.
+ * @param maxSize the maximum cache size
+ * @throws IllegalArgumentException if the supplied {@code maxSize} value
+ * is not positive
+ * @since 4.3
+ * @see #DefaultContextCache()
+ */
+ public DefaultContextCache(int maxSize) {
+ Assert.isTrue(maxSize > 0, "'maxSize' must be positive");
+ this.maxSize = maxSize;
+ }
+
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -182,6 +218,13 @@ public class DefaultContextCache implements ContextCache {
}
/**
+ * Get the maximum size of this cache.
+ */
+ public int getMaxSize() {
+ return this.maxSize;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -210,7 +253,7 @@ public class DefaultContextCache implements ContextCache {
*/
@Override
public void reset() {
- synchronized (contextMap) {
+ synchronized (this.contextMap) {
clear();
clearStatistics();
}
@@ -221,7 +264,7 @@ public class DefaultContextCache implements ContextCache {
*/
@Override
public void clear() {
- synchronized (contextMap) {
+ synchronized (this.contextMap) {
this.contextMap.clear();
this.hierarchyMap.clear();
}
@@ -232,7 +275,7 @@ public class DefaultContextCache implements ContextCache {
*/
@Override
public void clearStatistics() {
- synchronized (contextMap) {
+ synchronized (this.contextMap) {
this.hitCount.set(0);
this.missCount.set(0);
}
@@ -259,10 +302,44 @@ public class DefaultContextCache implements ContextCache {
public String toString() {
return new ToStringCreator(this)
.append("size", size())
+ .append("maxSize", getMaxSize())
.append("parentContextCount", getParentContextCount())
.append("hitCount", getHitCount())
.append("missCount", getMissCount())
.toString();
}
+
+ /**
+ * Simple cache implementation based on {@link LinkedHashMap} with a maximum
+ * size and a <em>least recently used</em> (LRU) eviction policy that
+ * properly closes application contexts.
+ * @since 4.3
+ */
+ @SuppressWarnings("serial")
+ private class LruCache extends LinkedHashMap<MergedContextConfiguration, ApplicationContext> {
+
+ /**
+ * Create a new {@code LruCache} with the supplied initial capacity
+ * and load factor.
+ * @param initialCapacity the initial capacity
+ * @param loadFactor the load factor
+ */
+ LruCache(int initialCapacity, float loadFactor) {
+ super(initialCapacity, loadFactor, true);
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<MergedContextConfiguration, ApplicationContext> eldest) {
+ if (this.size() > DefaultContextCache.this.getMaxSize()) {
+ // Do NOT delete "DefaultContextCache.this."; otherwise, we accidentally
+ // invoke java.util.Map.remove(Object, Object).
+ DefaultContextCache.this.remove(eldest.getKey(), HierarchyMode.CURRENT_LEVEL);
+ }
+
+ // Return false since we invoke a custom eviction algorithm.
+ return false;
+ }
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java
index 65fe0a83..5794dde3 100644
--- a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java
@@ -50,9 +50,7 @@ import org.springframework.core.annotation.AliasFor;
* multiple instances of {@code @Sql}.
*
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
- * <em>composed annotations</em>; however, attribute overrides are not currently
- * supported for {@linkplain Repeatable repeatable} annotations that are used as
- * meta-annotations.
+ * <em>composed annotations</em> with attribute overrides.
*
* @author Sam Brannen
* @since 4.1
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
index 71e0e1f6..c58867eb 100644
--- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -26,7 +26,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
-import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
@@ -129,10 +129,10 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
private void executeSqlScripts(TestContext testContext, ExecutionPhase executionPhase) throws Exception {
boolean classLevel = false;
- Set<Sql> sqlAnnotations = AnnotationUtils.getRepeatableAnnotations(testContext.getTestMethod(), Sql.class,
+ Set<Sql> sqlAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(testContext.getTestMethod(), Sql.class,
SqlGroup.class);
if (sqlAnnotations.isEmpty()) {
- sqlAnnotations = AnnotationUtils.getRepeatableAnnotations(testContext.getTestClass(), Sql.class,
+ sqlAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(testContext.getTestClass(), Sql.class,
SqlGroup.class);
if (!sqlAnnotations.isEmpty()) {
classLevel = true;
@@ -157,7 +157,6 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
* @param classLevel {@code true} if {@link Sql @Sql} was declared at the
* class level
*/
- @SuppressWarnings("serial")
private void executeSqlScripts(Sql sql, ExecutionPhase executionPhase, TestContext testContext, boolean classLevel)
throws Exception {
if (executionPhase != sql.executionPhase()) {
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java
index d16b05f8..474aef2c 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -61,16 +61,16 @@ import org.springframework.test.context.web.ServletTestExecutionListener;
* <ul>
* <li>If you do not wish for your test classes to be tied to a Spring-specific
* class hierarchy, you may configure your own custom test classes by using
- * {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration @ContextConfiguration},
+ * {@link SpringRunner}, {@link ContextConfiguration @ContextConfiguration},
* {@link TestExecutionListeners @TestExecutionListeners}, etc.</li>
* <li>If you wish to extend this class and use a runner other than the
- * {@link SpringJUnit4ClassRunner}, as of Spring Framework 4.2 you can use
+ * {@link SpringRunner}, as of Spring Framework 4.2 you can use
* {@link org.springframework.test.context.junit4.rules.SpringClassRule SpringClassRule} and
* {@link org.springframework.test.context.junit4.rules.SpringMethodRule SpringMethodRule}
* and specify your runner of choice via {@link RunWith @RunWith(...)}.</li>
* </ul>
*
- * <p><strong>NOTE:</strong> As of Spring Framework 4.1, this class requires JUnit 4.9 or higher.
+ * <p><strong>NOTE:</strong> As of Spring Framework 4.3, this class requires JUnit 4.12 or higher.
*
* @author Sam Brannen
* @since 2.5
@@ -85,7 +85,7 @@ import org.springframework.test.context.web.ServletTestExecutionListener;
* @see AbstractTransactionalJUnit4SpringContextTests
* @see org.springframework.test.context.testng.AbstractTestNGSpringContextTests
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@TestExecutionListeners({ ServletTestExecutionListener.class, DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
public abstract class AbstractJUnit4SpringContextTests implements ApplicationContextAware {
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
index 9745e597..b6c533de 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -64,16 +64,16 @@ import org.springframework.transaction.annotation.Transactional;
* <ul>
* <li>If you do not wish for your test classes to be tied to a Spring-specific
* class hierarchy, you may configure your own custom test classes by using
- * {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration @ContextConfiguration},
+ * {@link SpringRunner}, {@link ContextConfiguration @ContextConfiguration},
* {@link TestExecutionListeners @TestExecutionListeners}, etc.</li>
* <li>If you wish to extend this class and use a runner other than the
- * {@link SpringJUnit4ClassRunner}, as of Spring Framework 4.2 you can use
+ * {@link SpringRunner}, as of Spring Framework 4.2 you can use
* {@link org.springframework.test.context.junit4.rules.SpringClassRule SpringClassRule} and
* {@link org.springframework.test.context.junit4.rules.SpringMethodRule SpringMethodRule}
* and specify your runner of choice via {@link org.junit.runner.RunWith @RunWith(...)}.</li>
* </ul>
*
- * <p><strong>NOTE:</strong> As of Spring Framework 4.1, this class requires JUnit 4.9 or higher.
+ * <p><strong>NOTE:</strong> As of Spring Framework 4.3, this class requires JUnit 4.12 or higher.
*
* @author Sam Brannen
* @author Juergen Hoeller
@@ -83,7 +83,6 @@ import org.springframework.transaction.annotation.Transactional;
* @see org.springframework.test.context.TestExecutionListeners
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
* @see org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
- * @see org.springframework.test.context.transaction.TransactionConfiguration
* @see org.springframework.transaction.annotation.Transactional
* @see org.springframework.test.annotation.Commit
* @see org.springframework.test.annotation.Rollback
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java
index 1d6a11f9..a0ed3db9 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -18,6 +18,7 @@ package org.springframework.test.context.junit4;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -55,6 +56,9 @@ import org.springframework.util.ReflectionUtils;
* <em>Spring TestContext Framework</em> to standard JUnit tests by means of the
* {@link TestContextManager} and associated support classes and annotations.
*
+ * <p>To use this class, simply annotate a JUnit 4 based test class with
+ * {@code @RunWith(SpringJUnit4ClassRunner.class)} or {@code @RunWith(SpringRunner.class)}.
+ *
* <p>The following list constitutes all annotations currently supported directly
* or indirectly by {@code SpringJUnit4ClassRunner}. <em>(Note that additional
* annotations may be supported by various
@@ -75,11 +79,12 @@ import org.springframework.util.ReflectionUtils;
* <p>If you would like to use the Spring TestContext Framework with a runner
* other than this one, use {@link SpringClassRule} and {@link SpringMethodRule}.
*
- * <p><strong>NOTE:</strong> As of Spring Framework 4.1, this class requires JUnit 4.9 or higher.
+ * <p><strong>NOTE:</strong> As of Spring Framework 4.3, this class requires JUnit 4.12 or higher.
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
+ * @see SpringRunner
* @see TestContextManager
* @see AbstractJUnit4SpringContextTests
* @see AbstractTransactionalJUnit4SpringContextTests
@@ -92,27 +97,20 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
private static final Method withRulesMethod;
- // Used by RunAfterTestClassCallbacks and RunAfterTestMethodCallbacks
- private static final String MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME = "org.junit.runners.model.MultipleFailureException";
-
static {
- boolean junit4dot9Present = ClassUtils.isPresent(MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME,
- SpringJUnit4ClassRunner.class.getClassLoader());
- if (!junit4dot9Present) {
- throw new IllegalStateException(String.format(
- "Failed to find class [%s]: SpringJUnit4ClassRunner requires JUnit 4.9 or higher.",
- MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME));
+ if (!ClassUtils.isPresent("org.junit.internal.Throwables", SpringJUnit4ClassRunner.class.getClassLoader())) {
+ throw new IllegalStateException("SpringJUnit4ClassRunner requires JUnit 4.12 or higher.");
}
withRulesMethod = ReflectionUtils.findMethod(SpringJUnit4ClassRunner.class, "withRules",
FrameworkMethod.class, Object.class, Statement.class);
if (withRulesMethod == null) {
- throw new IllegalStateException(
- "Failed to find withRules() method: SpringJUnit4ClassRunner requires JUnit 4.9 or higher.");
+ throw new IllegalStateException("SpringJUnit4ClassRunner requires JUnit 4.12 or higher.");
}
ReflectionUtils.makeAccessible(withRulesMethod);
}
+
private final TestContextManager testContextManager;
@@ -376,8 +374,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
statement = new SpringFailOnTimeout(next, springTimeout);
}
else if (junitTimeout > 0) {
- // TODO Use FailOnTimeout.builder() once JUnit 4.12 is the minimum supported version.
- statement = new FailOnTimeout(next, junitTimeout);
+ statement = FailOnTimeout.builder().withTimeout(junitTimeout, TimeUnit.MILLISECONDS).build(next);
}
else {
statement = next;
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringRunner.java
new file mode 100644
index 00000000..37dad335
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringRunner.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2016 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.context.junit4;
+
+import org.junit.runners.model.InitializationError;
+
+/**
+ * {@code SpringRunner} is an <em>alias</em> for the {@link SpringJUnit4ClassRunner}.
+ *
+ * <p>To use this class, simply annotate a JUnit 4 based test class with
+ * {@code @RunWith(SpringRunner.class)}.
+ *
+ * <p>If you would like to use the Spring TestContext Framework with a runner other than
+ * this one, use {@link org.springframework.test.context.junit4.rules.SpringClassRule}
+ * and {@link org.springframework.test.context.junit4.rules.SpringMethodRule}.
+ *
+ * <p><strong>NOTE:</strong> This class requires JUnit 4.12 or higher.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ * @see SpringJUnit4ClassRunner
+ * @see org.springframework.test.context.junit4.rules.SpringClassRule
+ * @see org.springframework.test.context.junit4.rules.SpringMethodRule
+ */
+public final class SpringRunner extends SpringJUnit4ClassRunner {
+
+ /**
+ * Construct a new {@code SpringRunner} and initialize a
+ * {@link org.springframework.test.context.TestContextManager TestContextManager}
+ * to provide Spring testing functionality to standard JUnit 4 tests.
+ * @param clazz the test class to be run
+ * @see #createTestContextManager(Class)
+ */
+ public SpringRunner(Class<?> clazz) throws InitializationError {
+ super(clazz);
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/package-info.java b/spring-test/src/main/java/org/springframework/test/context/junit4/package-info.java
index d322cc36..0a93b268 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/package-info.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/package-info.java
@@ -1,4 +1,5 @@
/**
- * Support classes for integrating the <em>Spring TestContext Framework</em> with JUnit.
+ * Support classes for integrating the <em>Spring TestContext Framework</em>
+ * with JUnit 4.12 or higher.
*/
package org.springframework.test.context.junit4;
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java
index 904a3643..cbbe7437 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java
@@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -76,7 +77,7 @@ import org.springframework.util.ClassUtils;
* <li>{@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}</li>
* </ul>
*
- * <p><strong>NOTE:</strong> This class requires JUnit 4.9 or higher.
+ * <p><strong>NOTE:</strong> As of Spring Framework 4.3, this class requires JUnit 4.12 or higher.
*
* @author Sam Brannen
* @author Philippe Marschall
@@ -96,16 +97,9 @@ public class SpringClassRule implements TestRule {
private static final Map<Class<?>, TestContextManager> testContextManagerCache =
new ConcurrentHashMap<Class<?>, TestContextManager>(64);
- // Used by RunAfterTestClassCallbacks
- private static final String MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME = "org.junit.runners.model.MultipleFailureException";
-
static {
- boolean junit4dot9Present = ClassUtils.isPresent(MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME,
- SpringClassRule.class.getClassLoader());
- if (!junit4dot9Present) {
- throw new IllegalStateException(String.format(
- "Failed to find class [%s]: SpringClassRule requires JUnit 4.9 or higher.",
- MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME));
+ if (!ClassUtils.isPresent("org.junit.internal.Throwables", SpringClassRule.class.getClassLoader())) {
+ throw new IllegalStateException("SpringClassRule requires JUnit 4.12 or higher.");
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java
index 7bc200a0..89a97418 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java
@@ -20,6 +20,7 @@ import java.lang.reflect.Field;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.junit.ClassRule;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
@@ -79,7 +80,7 @@ import org.springframework.util.ReflectionUtils;
* <li>{@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}</li>
* </ul>
*
- * <p><strong>NOTE:</strong> This class requires JUnit 4.9 or higher.
+ * <p><strong>NOTE:</strong> As of Spring Framework 4.3, this class requires JUnit 4.12 or higher.
*
* @author Sam Brannen
* @author Philippe Marschall
@@ -93,16 +94,9 @@ public class SpringMethodRule implements MethodRule {
private static final Log logger = LogFactory.getLog(SpringMethodRule.class);
- // Used by RunAfterTestMethodCallbacks
- private static final String MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME = "org.junit.runners.model.MultipleFailureException";
-
static {
- boolean junit4dot9Present = ClassUtils.isPresent(MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME,
- SpringMethodRule.class.getClassLoader());
- if (!junit4dot9Present) {
- throw new IllegalStateException(String.format(
- "Failed to find class [%s]: SpringMethodRule requires JUnit 4.9 or higher.",
- MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME));
+ if (!ClassUtils.isPresent("org.junit.internal.Throwables", SpringMethodRule.class.getClassLoader())) {
+ throw new IllegalStateException("SpringMethodRule requires JUnit 4.12 or higher.");
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java
index 144c4154..5d53bbff 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -19,10 +19,10 @@ package org.springframework.test.context.junit4.statements;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
-import org.junit.Assume;
+import org.junit.AssumptionViolatedException;
import org.junit.runners.model.Statement;
-import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.annotation.ProfileValueUtils;
import org.springframework.util.Assert;
@@ -64,6 +64,7 @@ public class ProfileValueChecker extends Statement {
this.testMethod = testMethod;
}
+
/**
* Determine if the test specified by arguments to the
* {@linkplain #ProfileValueChecker constructor} is <em>enabled</em> in
@@ -76,27 +77,24 @@ public class ProfileValueChecker extends Statement {
* will simply evaluate the next {@link Statement} in the execution chain.
* @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class)
* @see ProfileValueUtils#isTestEnabledInThisEnvironment(Method, Class)
- * @see org.junit.Assume
+ * @throws AssumptionViolatedException if the test is disabled
+ * @throws Throwable if evaluation of the next statement fails
*/
@Override
public void evaluate() throws Throwable {
if (this.testMethod == null) {
if (!ProfileValueUtils.isTestEnabledInThisEnvironment(this.testClass)) {
- // Invoke assumeTrue() with false to avoid direct reference to JUnit's
- // AssumptionViolatedException which exists in two packages as of JUnit 4.12.
- Annotation ann = AnnotationUtils.findAnnotation(this.testClass, IfProfileValue.class);
- Assume.assumeTrue(String.format(
+ Annotation ann = AnnotatedElementUtils.findMergedAnnotation(this.testClass, IfProfileValue.class);
+ throw new AssumptionViolatedException(String.format(
"Profile configured via [%s] is not enabled in this environment for test class [%s].",
- ann, this.testClass.getName()), false);
+ ann, this.testClass.getName()));
}
}
else {
if (!ProfileValueUtils.isTestEnabledInThisEnvironment(this.testMethod, this.testClass)) {
- // Invoke assumeTrue() with false to avoid direct reference to JUnit's
- // AssumptionViolatedException which exists in two packages as of JUnit 4.12.
- Assume.assumeTrue(String.format(
+ throw new AssumptionViolatedException(String.format(
"Profile configured via @IfProfileValue is not enabled in this environment for test method [%s].",
- this.testMethod), false);
+ this.testMethod));
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java
index e7946dd6..bf05536a 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java
@@ -80,13 +80,7 @@ public class RunAfterTestClassCallbacks extends Statement {
errors.add(ex);
}
- if (errors.isEmpty()) {
- return;
- }
- if (errors.size() == 1) {
- throw errors.get(0);
- }
- throw new MultipleFailureException(errors);
+ MultipleFailureException.assertEmpty(errors);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java
index 1a1aa877..e0771264 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java
@@ -97,13 +97,7 @@ public class RunAfterTestMethodCallbacks extends Statement {
errors.add(ex);
}
- if (errors.isEmpty()) {
- return;
- }
- if (errors.size() == 1) {
- throw errors.get(0);
- }
- throw new MultipleFailureException(errors);
+ MultipleFailureException.assertEmpty(errors);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
index 6994c798..f17782f8 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
@@ -33,6 +33,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.ContextConfigurationAttributes;
+import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.SmartContextLoader;
@@ -56,10 +57,13 @@ import org.springframework.util.ResourceUtils;
*
* @author Sam Brannen
* @author Juergen Hoeller
+ * @author Phillip Webb
* @since 2.5
* @see #generateDefaultLocations
* @see #getResourceSuffixes
* @see #modifyLocations
+ * @see #prepareContext
+ * @see #customizeContext
*/
public abstract class AbstractContextLoader implements SmartContextLoader {
@@ -110,12 +114,13 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
* <li>Determines what (if any) context initializer classes have been supplied
* via the {@code MergedContextConfiguration} and instantiates and
* {@linkplain ApplicationContextInitializer#initialize invokes} each with the
- * given application context.</li>
+ * given application context.
* <ul>
* <li>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or annotated with {@link
* org.springframework.core.annotation.Order @Order} will be sorted appropriately.</li>
* </ul>
+ * </li>
* </ul>
* @param context the newly created application context
* @param mergedConfig the merged context configuration
@@ -166,6 +171,23 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
}
}
+ /**
+ * Customize the {@link ConfigurableApplicationContext} created by this
+ * {@code ContextLoader} <em>after</em> bean definitions have been loaded
+ * into the context but <em>before</em> the context has been refreshed.
+ * <p>The default implementation delegates to all
+ * {@link MergedContextConfiguration#getContextCustomizers context customizers}
+ * that have been registered with the supplied {@code mergedConfig}.
+ * @param context the newly created application context
+ * @param mergedConfig the merged context configuration
+ * @since 4.3
+ */
+ protected void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
+ for (ContextCustomizer contextCustomizer : mergedConfig.getContextCustomizers()) {
+ contextCustomizer.customizeContext(context, mergedConfig);
+ }
+ }
+
// --- ContextLoader -------------------------------------------------------
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java
index ca60c8dd..c0ae608e 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -29,7 +29,6 @@ import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.SmartContextLoader;
import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
/**
* {@code AbstractDelegatingSmartContextLoader} serves as an abstract base class
@@ -202,15 +201,6 @@ public abstract class AbstractDelegatingSmartContextLoader implements SmartConte
name(getAnnotationConfigLoader()), configAttributes));
}
- // If neither loader detected defaults and no initializers were declared,
- // throw an exception.
- if (!configAttributes.hasResources() && ObjectUtils.isEmpty(configAttributes.getInitializers())) {
- throw new IllegalStateException(String.format(
- "Neither %s nor %s was able to detect defaults, and no ApplicationContextInitializers "
- + "were declared for context configuration %s", name(getXmlLoader()),
- name(getAnnotationConfigLoader()), configAttributes));
- }
-
if (configAttributes.hasLocations() && configAttributes.hasClasses()) {
String message = String.format(
"Configuration error: both default locations AND default configuration classes "
@@ -263,8 +253,9 @@ public abstract class AbstractDelegatingSmartContextLoader implements SmartConte
}
// If neither of the candidates supports the mergedConfig based on resources but
- // ACIs were declared, then delegate to the annotation config loader.
- if (!mergedConfig.getContextInitializerClasses().isEmpty()) {
+ // ACIs or customizers were declared, then delegate to the annotation config
+ // loader.
+ if (!mergedConfig.getContextInitializerClasses().isEmpty() || !mergedConfig.getContextCustomizers().isEmpty()) {
return delegateLoading(getAnnotationConfigLoader(), mergedConfig);
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java
index 41e42fcf..4bc65d29 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java
@@ -16,7 +16,6 @@
package org.springframework.test.context.support;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -53,6 +52,7 @@ import org.springframework.util.StringUtils;
*
* @author Sam Brannen
* @author Juergen Hoeller
+ * @author Phillip Webb
* @since 2.5
* @see #loadContext(MergedContextConfiguration)
* @see #loadContext(String...)
@@ -92,6 +92,8 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
* annotation configuration processors.</li>
* <li>Calls {@link #customizeContext(GenericApplicationContext)} to allow for customizing the context
* before it is refreshed.</li>
+ * <li>Calls {@link #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)} to
+ * allow for customizing the context before it is refreshed.</li>
* <li>{@link ConfigurableApplicationContext#refresh Refreshes} the
* context and registers a JVM shutdown hook for it.</li>
* </ul>
@@ -122,6 +124,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
loadBeanDefinitions(context, mergedConfig);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
customizeContext(context);
+ customizeContext(context, mergedConfig);
context.refresh();
context.registerShutdownHook();
return context;
@@ -205,6 +208,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
* @see GenericApplicationContext#setAllowBeanDefinitionOverriding
* @see GenericApplicationContext#setResourceLoader
* @see GenericApplicationContext#setId
+ * @see #prepareContext(ConfigurableApplicationContext, MergedContextConfiguration)
* @since 2.5
*/
protected void prepareContext(GenericApplicationContext context) {
@@ -278,6 +282,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
* @param context the newly created application context
* @see #loadContext(MergedContextConfiguration)
* @see #loadContext(String...)
+ * @see #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)
* @since 2.5
*/
protected void customizeContext(GenericApplicationContext context) {
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
index 7049115c..fb9e28c0 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
@@ -18,6 +18,7 @@ package org.springframework.test.context.support;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -30,8 +31,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
-import org.springframework.context.ApplicationContextInitializer;
-import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.support.SpringFactoriesLoader;
@@ -39,6 +38,8 @@ import org.springframework.test.context.BootstrapContext;
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfigurationAttributes;
+import org.springframework.test.context.ContextCustomizer;
+import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
@@ -71,6 +72,7 @@ import org.springframework.util.StringUtils;
*
* @author Sam Brannen
* @author Juergen Hoeller
+ * @author Phillip Webb
* @since 4.1
*/
public abstract class AbstractTestContextBootstrapper implements TestContextBootstrapper {
@@ -272,11 +274,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(
testClass, ContextConfiguration.class, ContextHierarchy.class) == null) {
- if (logger.isInfoEnabled()) {
- logger.info(String.format("Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
- testClass.getName()));
- }
- return new MergedContextConfiguration(testClass, null, null, null, null);
+ return buildDefaultMergedContextConfiguration(testClass, cacheAwareContextLoaderDelegate);
}
if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) {
@@ -296,7 +294,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Class<?> declaringClass = reversedList.get(0).getDeclaringClass();
mergedConfig = buildMergedContextConfiguration(
- declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate);
+ declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate, true);
parentConfig = mergedConfig;
}
@@ -306,8 +304,24 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
else {
return buildMergedContextConfiguration(testClass,
ContextLoaderUtils.resolveContextConfigurationAttributes(testClass),
- null, cacheAwareContextLoaderDelegate);
+ null, cacheAwareContextLoaderDelegate, true);
+ }
+ }
+
+ private MergedContextConfiguration buildDefaultMergedContextConfiguration(Class<?> testClass,
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
+
+ List<ContextConfigurationAttributes> defaultConfigAttributesList =
+ Collections.singletonList(new ContextConfigurationAttributes(testClass));
+
+ ContextLoader contextLoader = resolveContextLoader(testClass, defaultConfigAttributesList);
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format(
+ "Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s], using %s",
+ testClass.getName(), contextLoader.getClass().getSimpleName()));
}
+ return buildMergedContextConfiguration(testClass, defaultConfigAttributesList, null,
+ cacheAwareContextLoaderDelegate, false);
}
/**
@@ -323,6 +337,9 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* context in a context hierarchy, or {@code null} if there is no parent
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to
* be passed to the {@code MergedContextConfiguration} constructor
+ * @param requireLocationsClassesOrInitializers whether locations, classes, or
+ * initializers are required; typically {@code true} but may be set to {@code false}
+ * if the configured loader supports empty configuration
* @return the merged context configuration
* @see #resolveContextLoader
* @see ContextLoaderUtils#resolveContextConfigurationAttributes
@@ -334,48 +351,90 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
*/
private MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributesList, MergedContextConfiguration parentConfig,
- CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
+ boolean requireLocationsClassesOrInitializers) {
+
+ Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be null or empty");
ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList);
- List<String> locationsList = new ArrayList<String>();
- List<Class<?>> classesList = new ArrayList<Class<?>>();
+ List<String> locations = new ArrayList<String>();
+ List<Class<?>> classes = new ArrayList<Class<?>>();
+ List<Class<?>> initializers = new ArrayList<Class<?>>();
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Processing locations and classes for context configuration attributes %s",
- configAttributes));
+ configAttributes));
}
if (contextLoader instanceof SmartContextLoader) {
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
smartContextLoader.processContextConfiguration(configAttributes);
- locationsList.addAll(0, Arrays.asList(configAttributes.getLocations()));
- classesList.addAll(0, Arrays.asList(configAttributes.getClasses()));
+ locations.addAll(0, Arrays.asList(configAttributes.getLocations()));
+ classes.addAll(0, Arrays.asList(configAttributes.getClasses()));
}
else {
- String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
- configAttributes.getLocations());
- locationsList.addAll(0, Arrays.asList(processedLocations));
+ String[] processedLocations = contextLoader.processLocations(
+ configAttributes.getDeclaringClass(), configAttributes.getLocations());
+ locations.addAll(0, Arrays.asList(processedLocations));
// Legacy ContextLoaders don't know how to process classes
}
+ initializers.addAll(0, Arrays.asList(configAttributes.getInitializers()));
if (!configAttributes.isInheritLocations()) {
break;
}
}
- String[] locations = StringUtils.toStringArray(locationsList);
- Class<?>[] classes = ClassUtils.toClassArray(classesList);
- Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = //
- ApplicationContextInitializerUtils.resolveInitializerClasses(configAttributesList);
- String[] activeProfiles = ActiveProfilesUtils.resolveActiveProfiles(testClass);
- MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils.buildMergedTestPropertySources(testClass);
+ Set<ContextCustomizer> contextCustomizers = getContextCustomizers(testClass,
+ Collections.unmodifiableList(configAttributesList));
- MergedContextConfiguration mergedConfig = new MergedContextConfiguration(testClass, locations, classes,
- initializerClasses, activeProfiles, mergedTestPropertySources.getLocations(),
- mergedTestPropertySources.getProperties(), contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
+ if (requireLocationsClassesOrInitializers &&
+ areAllEmpty(locations, classes, initializers, contextCustomizers)) {
+ throw new IllegalStateException(String.format(
+ "%s was unable to detect defaults, and no ApplicationContextInitializers " +
+ "or ContextCustomizers were declared for context configuration attributes %s",
+ contextLoader.getClass().getSimpleName(), configAttributesList));
+ }
+
+ MergedTestPropertySources mergedTestPropertySources =
+ TestPropertySourceUtils.buildMergedTestPropertySources(testClass);
+ MergedContextConfiguration mergedConfig = new MergedContextConfiguration(testClass,
+ StringUtils.toStringArray(locations),
+ ClassUtils.toClassArray(classes),
+ ApplicationContextInitializerUtils.resolveInitializerClasses(configAttributesList),
+ ActiveProfilesUtils.resolveActiveProfiles(testClass),
+ mergedTestPropertySources.getLocations(),
+ mergedTestPropertySources.getProperties(),
+ contextCustomizers, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
return processMergedContextConfiguration(mergedConfig);
}
+ private Set<ContextCustomizer> getContextCustomizers(Class<?> testClass,
+ List<ContextConfigurationAttributes> configAttributes) {
+
+ List<ContextCustomizerFactory> factories = getContextCustomizerFactories();
+ Set<ContextCustomizer> customizers = new LinkedHashSet<ContextCustomizer>(factories.size());
+ for (ContextCustomizerFactory factory : factories) {
+ ContextCustomizer customizer = factory.createContextCustomizer(testClass, configAttributes);
+ if (customizer != null) {
+ customizers.add(customizer);
+ }
+ }
+ return customizers;
+ }
+
+ /**
+ * Get the {@link ContextCustomizerFactory} instances for this bootstrapper.
+ * <p>The default implementation uses the {@link SpringFactoriesLoader} mechanism
+ * for loading factories configured in all {@code META-INF/spring.factories}
+ * files on the classpath.
+ * @since 4.3
+ * @see SpringFactoriesLoader#loadFactories
+ */
+ protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
+ return SpringFactoriesLoader.loadFactories(ContextCustomizerFactory.class, getClass().getClassLoader());
+ }
+
/**
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
* supplied list of {@link ContextConfigurationAttributes} and then instantiate
@@ -388,7 +447,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* @param testClass the test class for which the {@code ContextLoader} should be
* resolved; must not be {@code null}
* @param configAttributesList the list of configuration attributes to process; must
- * not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
+ * not be {@code null}; must be ordered <em>bottom-up</em>
* (i.e., as if we were traversing up the class hierarchy)
* @return the resolved {@code ContextLoader} for the supplied {@code testClass}
* (never {@code null})
@@ -399,7 +458,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
List<ContextConfigurationAttributes> configAttributesList) {
Assert.notNull(testClass, "Class must not be null");
- Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
+ Assert.notNull(configAttributesList, "ContextConfigurationAttributes list must not be null");
Class<? extends ContextLoader> contextLoaderClass = resolveExplicitContextLoaderClass(configAttributesList);
if (contextLoaderClass == null) {
@@ -428,7 +487,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* step #1.</li>
* </ol>
* @param configAttributesList the list of configuration attributes to process;
- * must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
+ * must not be {@code null}; must be ordered <em>bottom-up</em>
* (i.e., as if we were traversing up the class hierarchy)
* @return the {@code ContextLoader} class to use for the supplied configuration
* attributes, or {@code null} if no explicit loader is found
@@ -438,7 +497,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
protected Class<? extends ContextLoader> resolveExplicitContextLoaderClass(
List<ContextConfigurationAttributes> configAttributesList) {
- Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be empty");
+ Assert.notNull(configAttributesList, "ContextConfigurationAttributes list must not be null");
+
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Resolving ContextLoader for context configuration attributes %s",
@@ -497,4 +557,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
return mergedConfig;
}
+
+ private static boolean areAllEmpty(Collection<?>... collections) {
+ for (Collection<?> collection : collections) {
+ if (!collection.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java
index baa15001..968d2300 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoaderUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -24,7 +24,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.SmartContextLoader;
import org.springframework.util.Assert;
@@ -103,7 +103,7 @@ public abstract class AnnotationConfigContextLoaderUtils {
*/
private static boolean isDefaultConfigurationClassCandidate(Class<?> clazz) {
return (clazz != null && isStaticNonPrivateAndNonFinal(clazz) &&
- (AnnotationUtils.findAnnotation(clazz, Configuration.class) != null));
+ AnnotatedElementUtils.hasAnnotation(clazz, Configuration.class));
}
private static boolean isStaticNonPrivateAndNonFinal(Class<?> clazz) {
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java
index da3383cb..49819fd5 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/ContextLoaderUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -81,13 +81,12 @@ abstract class ContextLoaderUtils {
* (must not be {@code null})
* @return the list of lists of configuration attributes for the specified class;
* never {@code null}
- * @throws IllegalArgumentException if the supplied class is {@code null}; if
+ * @throws IllegalArgumentException if the supplied class is {@code null}; or if
* neither {@code @ContextConfiguration} nor {@code @ContextHierarchy} is
- * <em>present</em> on the supplied class; or if a test class or composed annotation
+ * <em>present</em> on the supplied class
+ * @throws IllegalStateException if a test class or composed annotation
* in the class hierarchy declares both {@code @ContextConfiguration} and
* {@code @ContextHierarchy} as top-level annotations.
- * @throws IllegalStateException if no class in the class hierarchy declares
- * {@code @ContextHierarchy}.
* @since 3.2.2
* @see #buildContextHierarchyMap(Class)
* @see #resolveContextConfigurationAttributes(Class)
@@ -95,11 +94,10 @@ abstract class ContextLoaderUtils {
@SuppressWarnings("unchecked")
static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttributes(Class<?> testClass) {
Assert.notNull(testClass, "Class must not be null");
- Assert.state(findAnnotation(testClass, ContextHierarchy.class) != null, "@ContextHierarchy must be present");
- final Class<ContextConfiguration> contextConfigType = ContextConfiguration.class;
- final Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class;
- final List<List<ContextConfigurationAttributes>> hierarchyAttributes = new ArrayList<List<ContextConfigurationAttributes>>();
+ Class<ContextConfiguration> contextConfigType = ContextConfiguration.class;
+ Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class;
+ List<List<ContextConfigurationAttributes>> hierarchyAttributes = new ArrayList<List<ContextConfigurationAttributes>>();
UntypedAnnotationDescriptor desc =
findAnnotationDescriptorForTypes(testClass, contextConfigType, contextHierarchyType);
@@ -124,7 +122,7 @@ abstract class ContextLoaderUtils {
throw new IllegalStateException(msg);
}
- final List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>();
+ List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>();
if (contextConfigDeclaredLocally) {
ContextConfiguration contextConfiguration = AnnotationUtils.synthesizeAnnotation(
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
index 1619ea84..98e9c936 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -35,10 +35,11 @@ import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.util.TestContextResourceUtils;
-import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
+import org.springframework.test.util.MetaAnnotationUtils.*;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -57,19 +58,15 @@ import static org.springframework.test.util.MetaAnnotationUtils.*;
*/
public abstract class TestPropertySourceUtils {
- private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class);
-
/**
* The name of the {@link MapPropertySource} created from <em>inlined properties</em>.
* @since 4.1.5
- * @see {@link #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])}
+ * @see #addInlinedPropertiesToEnvironment
*/
public static final String INLINED_PROPERTIES_PROPERTY_SOURCE_NAME = "Inlined Test Properties";
+ private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class);
- private TestPropertySourceUtils() {
- /* no-op */
- }
static MergedTestPropertySources buildMergedTestPropertySources(Class<?> testClass) {
Class<TestPropertySource> annotationType = TestPropertySource.class;
@@ -78,7 +75,6 @@ public abstract class TestPropertySourceUtils {
return new MergedTestPropertySources();
}
- // else...
List<TestPropertySourceAttributes> attributesList = resolveTestPropertySourceAttributes(testClass);
String[] locations = mergeLocations(attributesList);
String[] properties = mergeProperties(attributesList);
@@ -87,30 +83,27 @@ public abstract class TestPropertySourceUtils {
private static List<TestPropertySourceAttributes> resolveTestPropertySourceAttributes(Class<?> testClass) {
Assert.notNull(testClass, "Class must not be null");
+ List<TestPropertySourceAttributes> attributesList = new ArrayList<TestPropertySourceAttributes>();
+ Class<TestPropertySource> annotationType = TestPropertySource.class;
- final List<TestPropertySourceAttributes> attributesList = new ArrayList<TestPropertySourceAttributes>();
- final Class<TestPropertySource> annotationType = TestPropertySource.class;
AnnotationDescriptor<TestPropertySource> descriptor = findAnnotationDescriptor(testClass, annotationType);
Assert.notNull(descriptor, String.format(
- "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
- annotationType.getName(), testClass.getName()));
+ "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
+ annotationType.getName(), testClass.getName()));
while (descriptor != null) {
TestPropertySource testPropertySource = descriptor.synthesizeAnnotation();
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
-
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @TestPropertySource [%s] for declaring class [%s].",
testPropertySource, rootDeclaringClass.getName()));
}
-
- TestPropertySourceAttributes attributes = new TestPropertySourceAttributes(rootDeclaringClass,
- testPropertySource);
+ TestPropertySourceAttributes attributes =
+ new TestPropertySourceAttributes(rootDeclaringClass, testPropertySource);
if (logger.isTraceEnabled()) {
logger.trace("Resolved TestPropertySource attributes: " + attributes);
}
attributesList.add(attributes);
-
descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType);
}
@@ -119,74 +112,90 @@ public abstract class TestPropertySourceUtils {
private static String[] mergeLocations(List<TestPropertySourceAttributes> attributesList) {
final List<String> locations = new ArrayList<String>();
-
for (TestPropertySourceAttributes attrs : attributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Processing locations for TestPropertySource attributes %s", attrs));
}
-
String[] locationsArray = TestContextResourceUtils.convertToClasspathResourcePaths(
- attrs.getDeclaringClass(), attrs.getLocations());
+ attrs.getDeclaringClass(), attrs.getLocations());
locations.addAll(0, Arrays.<String> asList(locationsArray));
-
if (!attrs.isInheritLocations()) {
break;
}
}
-
return StringUtils.toStringArray(locations);
}
private static String[] mergeProperties(List<TestPropertySourceAttributes> attributesList) {
final List<String> properties = new ArrayList<String>();
-
for (TestPropertySourceAttributes attrs : attributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Processing inlined properties for TestPropertySource attributes %s", attrs));
}
-
- properties.addAll(0, Arrays.<String> asList(attrs.getProperties()));
-
+ properties.addAll(0, Arrays.<String>asList(attrs.getProperties()));
if (!attrs.isInheritProperties()) {
break;
}
}
-
return StringUtils.toStringArray(properties);
}
/**
* Add the {@link Properties} files from the given resource {@code locations}
* to the {@link Environment} of the supplied {@code context}.
+ * <p>This method simply delegates to
+ * {@link #addPropertiesFilesToEnvironment(ConfigurableEnvironment, ResourceLoader, String...)}.
+ * @param context the application context whose environment should be updated;
+ * never {@code null}
+ * @param locations the resource locations of {@code Properties} files to add
+ * to the environment; potentially empty but never {@code null}
+ * @since 4.1.5
+ * @see ResourcePropertySource
+ * @see TestPropertySource#locations
+ * @see #addPropertiesFilesToEnvironment(ConfigurableEnvironment, ResourceLoader, String...)
+ * @throws IllegalStateException if an error occurs while processing a properties file
+ */
+ public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContext context, String... locations) {
+ Assert.notNull(context, "'context' must not be null");
+ Assert.notNull(locations, "'locations' must not be null");
+ addPropertiesFilesToEnvironment(context.getEnvironment(), context, locations);
+ }
+
+ /**
+ * Add the {@link Properties} files from the given resource {@code locations}
+ * to the supplied {@link ConfigurableEnvironment environment}.
* <p>Property placeholders in resource locations (i.e., <code>${...}</code>)
* will be {@linkplain Environment#resolveRequiredPlaceholders(String) resolved}
* against the {@code Environment}.
* <p>Each properties file will be converted to a {@link ResourcePropertySource}
* that will be added to the {@link PropertySources} of the environment with
* highest precedence.
- * @param context the application context whose environment should be updated;
+ * @param environment the environment to update; never {@code null}
+ * @param resourceLoader the {@code ResourceLoader} to use to load each resource;
* never {@code null}
* @param locations the resource locations of {@code Properties} files to add
* to the environment; potentially empty but never {@code null}
- * @since 4.1.5
+ * @since 4.3
* @see ResourcePropertySource
* @see TestPropertySource#locations
+ * @see #addPropertiesFilesToEnvironment(ConfigurableApplicationContext, String...)
* @throws IllegalStateException if an error occurs while processing a properties file
*/
- public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContext context,
- String[] locations) {
- Assert.notNull(context, "context must not be null");
- Assert.notNull(locations, "locations must not be null");
+ public static void addPropertiesFilesToEnvironment(ConfigurableEnvironment environment,
+ ResourceLoader resourceLoader, String... locations) {
+
+ Assert.notNull(environment, "'environment' must not be null");
+ Assert.notNull(resourceLoader, "'resourceLoader' must not be null");
+ Assert.notNull(locations, "'locations' must not be null");
try {
- ConfigurableEnvironment environment = context.getEnvironment();
for (String location : locations) {
String resolvedLocation = environment.resolveRequiredPlaceholders(location);
- Resource resource = context.getResource(resolvedLocation);
+ Resource resource = resourceLoader.getResource(resolvedLocation);
environment.getPropertySources().addFirst(new ResourcePropertySource(resource));
}
}
- catch (IOException e) {
- throw new IllegalStateException("Failed to add PropertySource to Environment", e);
+ catch (IOException ex) {
+ throw new IllegalStateException("Failed to add PropertySource to Environment", ex);
}
}
@@ -203,10 +212,9 @@ public abstract class TestPropertySourceUtils {
* @see TestPropertySource#properties
* @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])
*/
- public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context,
- String[] inlinedProperties) {
- Assert.notNull(context, "context must not be null");
- Assert.notNull(inlinedProperties, "inlinedProperties must not be null");
+ public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context, String... inlinedProperties) {
+ Assert.notNull(context, "'context' must not be null");
+ Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null");
addInlinedPropertiesToEnvironment(context.getEnvironment(), inlinedProperties);
}
@@ -226,17 +234,22 @@ public abstract class TestPropertySourceUtils {
* @see TestPropertySource#properties
* @see #convertInlinedPropertiesToMap
*/
- public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment environment, String[] inlinedProperties) {
- Assert.notNull(environment, "environment must not be null");
- Assert.notNull(inlinedProperties, "inlinedProperties must not be null");
+ public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment environment, String... inlinedProperties) {
+ Assert.notNull(environment, "'environment' must not be null");
+ Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null");
if (!ObjectUtils.isEmpty(inlinedProperties)) {
if (logger.isDebugEnabled()) {
- logger.debug("Adding inlined properties to environment: "
- + ObjectUtils.nullSafeToString(inlinedProperties));
+ logger.debug("Adding inlined properties to environment: " +
+ ObjectUtils.nullSafeToString(inlinedProperties));
+ }
+ MapPropertySource ps = (MapPropertySource)
+ environment.getPropertySources().get(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
+ if (ps == null) {
+ ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME,
+ new LinkedHashMap<String, Object>());
+ environment.getPropertySources().addFirst(ps);
}
- MapPropertySource ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME,
- convertInlinedPropertiesToMap(inlinedProperties));
- environment.getPropertySources().addFirst(ps);
+ ps.getSource().putAll(convertInlinedPropertiesToMap(inlinedProperties));
}
}
@@ -257,24 +270,22 @@ public abstract class TestPropertySourceUtils {
* a given inlined property contains multiple key-value pairs
* @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])
*/
- public static Map<String, Object> convertInlinedPropertiesToMap(String[] inlinedProperties) {
- Assert.notNull(inlinedProperties, "inlinedProperties must not be null");
+ public static Map<String, Object> convertInlinedPropertiesToMap(String... inlinedProperties) {
+ Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null");
Map<String, Object> map = new LinkedHashMap<String, Object>();
-
Properties props = new Properties();
+
for (String pair : inlinedProperties) {
if (!StringUtils.hasText(pair)) {
continue;
}
-
try {
props.load(new StringReader(pair));
}
- catch (Exception e) {
- throw new IllegalStateException("Failed to load test environment property from [" + pair + "].", e);
+ catch (Exception ex) {
+ throw new IllegalStateException("Failed to load test environment property from [" + pair + "]", ex);
}
- Assert.state(props.size() == 1, "Failed to load exactly one test environment property from [" + pair + "].");
-
+ Assert.state(props.size() == 1, "Failed to load exactly one test environment property from [" + pair + "]");
for (String name : props.stringPropertyNames()) {
map.put(name, props.getProperty(name));
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
index 1fd08e32..0899e13b 100644
--- a/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
+++ b/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -67,7 +67,6 @@ import org.springframework.transaction.annotation.Transactional;
* @see org.springframework.test.context.TestExecutionListeners
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
* @see org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
- * @see org.springframework.test.context.transaction.TransactionConfiguration
* @see org.springframework.transaction.annotation.Transactional
* @see org.springframework.test.annotation.Commit
* @see org.springframework.test.annotation.Rollback
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java b/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java
index 04ebe481..a7f6653e 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -23,12 +23,16 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * <p>Test annotation to indicate that the annotated {@code public void} method
+ * <p>Test annotation which indicates that the annotated {@code void} method
* should be executed <em>after</em> a transaction is ended for a test method
- * configured to run within a transaction via the {@code @Transactional} annotation.
+ * configured to run within a transaction via Spring's {@code @Transactional}
+ * annotation.
*
- * <p>The {@code @AfterTransaction} methods of superclasses will be executed
- * after those of the current class.
+ * <p>As of Spring Framework 4.3, {@code @AfterTransaction} may be declared on
+ * Java 8 based interface default methods.
+ *
+ * <p>{@code @AfterTransaction} methods declared in superclasses or as interface
+ * default methods will be executed after those of the current test class.
*
* <p>As of Spring Framework 4.0, this annotation may be used as a
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java b/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java
index b7110015..217c7d17 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -23,12 +23,16 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * <p>Test annotation to indicate that the annotated {@code public void} method
+ * <p>Test annotation which indicates that the annotated {@code void} method
* should be executed <em>before</em> a transaction is started for a test method
- * configured to run within a transaction via the {@code @Transactional} annotation.
+ * configured to run within a transaction via Spring's {@code @Transactional}
+ * annotation.
*
- * <p>The {@code @BeforeTransaction} methods of superclasses will be executed
- * before those of the current class.
+ * <p>As of Spring Framework 4.3, {@code @BeforeTransaction} may be declared on
+ * Java 8 based interface default methods.
+ *
+ * <p>{@code @BeforeTransaction} methods declared in superclasses or as interface
+ * default methods will be executed before those of the current test class.
*
* <p>As of Spring Framework 4.0, this annotation may be used as a
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java
index 3b65d690..b00a9955 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -17,6 +17,7 @@
package org.springframework.test.context.transaction;
import java.util.Map;
+
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
@@ -73,7 +74,8 @@ public abstract class TestContextTransactionUtils {
* <li>Look up the {@code DataSource} by type and name, if the supplied
* {@code name} is non-empty, throwing a {@link BeansException} if the named
* {@code DataSource} does not exist.
- * <li>Attempt to look up a single {@code DataSource} by type.
+ * <li>Attempt to look up the single {@code DataSource} by type.
+ * <li>Attempt to look up the <em>primary</em> {@code DataSource} by type.
* <li>Attempt to look up the {@code DataSource} by type and the
* {@linkplain #DEFAULT_DATA_SOURCE_NAME default data source name}.
* @param testContext the test context for which the {@code DataSource}
@@ -110,15 +112,21 @@ public abstract class TestContextTransactionUtils {
if (dataSources.size() == 1) {
return dataSources.values().iterator().next();
}
+
+ try {
+ // look up single bean by type, with support for 'primary' beans
+ return bf.getBean(DataSource.class);
+ }
+ catch (BeansException ex) {
+ logBeansException(testContext, ex, PlatformTransactionManager.class);
+ }
}
// look up by type and default name
return bf.getBean(DEFAULT_DATA_SOURCE_NAME, DataSource.class);
}
catch (BeansException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("Caught exception while retrieving DataSource for test context " + testContext, ex);
- }
+ logBeansException(testContext, ex, DataSource.class);
return null;
}
}
@@ -133,7 +141,8 @@ public abstract class TestContextTransactionUtils {
* <li>Look up the transaction manager by type and explicit name, if the supplied
* {@code name} is non-empty, throwing a {@link BeansException} if the named
* transaction manager does not exist.
- * <li>Attempt to look up the transaction manager by type.
+ * <li>Attempt to look up the single transaction manager by type.
+ * <li>Attempt to look up the <em>primary</em> transaction manager by type.
* <li>Attempt to look up the transaction manager via a
* {@link TransactionManagementConfigurer}, if present.
* <li>Attempt to look up the transaction manager by type and the
@@ -176,6 +185,14 @@ public abstract class TestContextTransactionUtils {
return txMgrs.values().iterator().next();
}
+ try {
+ // look up single bean by type, with support for 'primary' beans
+ return bf.getBean(PlatformTransactionManager.class);
+ }
+ catch (BeansException ex) {
+ logBeansException(testContext, ex, PlatformTransactionManager.class);
+ }
+
// look up single TransactionManagementConfigurer
Map<String, TransactionManagementConfigurer> configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
lbf, TransactionManagementConfigurer.class);
@@ -192,14 +209,18 @@ public abstract class TestContextTransactionUtils {
return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class);
}
catch (BeansException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("Caught exception while retrieving transaction manager for test context " + testContext,
- ex);
- }
+ logBeansException(testContext, ex, PlatformTransactionManager.class);
return null;
}
}
+ private static void logBeansException(TestContext testContext, BeansException ex, Class<?> beanType) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Caught exception while retrieving %s for test context %s",
+ beanType.getSimpleName(), testContext), ex);
+ }
+ }
+
/**
* Create a delegating {@link TransactionAttribute} for the supplied target
* {@link TransactionAttribute} and {@link TestContext}, using the names of
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java
index 98857e5a..e35e862e 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -40,8 +40,9 @@ import java.lang.annotation.Target;
* @see org.springframework.test.context.jdbc.SqlConfig
* @see org.springframework.test.context.jdbc.SqlConfig#transactionManager
* @see org.springframework.test.context.ContextConfiguration
- * @deprecated As of Spring Framework 4.2, use {@code @Rollback} at the class
- * level and the {@code transactionManager} qualifier in {@code @Transactional}.
+ * @deprecated As of Spring Framework 4.2, use {@code @Rollback} or
+ * {@code @Commit} at the class level and the {@code transactionManager}
+ * qualifier in {@code @Transactional}.
*/
@Deprecated
@Documented
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
index 6ae8d4bd..356eb3d0 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -30,6 +30,8 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.test.annotation.Commit;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
@@ -43,8 +45,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
-import static org.springframework.core.annotation.AnnotationUtils.*;
-
/**
* {@code TestExecutionListener} that provides support for executing tests
* within <em>test-managed transactions</em> by honoring Spring's
@@ -83,8 +83,9 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
* <h3>Declarative Rollback and Commit Behavior</h3>
* <p>By default, test transactions will be automatically <em>rolled back</em>
* after completion of the test; however, transactional commit and rollback
- * behavior can be configured declaratively via the {@link Rollback @Rollback}
- * annotation at the class level and at the method level.
+ * behavior can be configured declaratively via the {@link Commit @Commit}
+ * and {@link Rollback @Rollback} annotations at the class level and at the
+ * method level.
*
* <h3>Programmatic Transaction Management</h3>
* <p>As of Spring Framework 4.1, it is possible to interact with test-managed
@@ -96,9 +97,10 @@ import static org.springframework.core.annotation.AnnotationUtils.*;
* <p>When executing transactional tests, it is sometimes useful to be able to
* execute certain <em>set up</em> or <em>tear down</em> code outside of a
* transaction. {@code TransactionalTestExecutionListener} provides such
- * support for methods annotated with
- * {@link BeforeTransaction @BeforeTransaction} or
- * {@link AfterTransaction @AfterTransaction}.
+ * support for methods annotated with {@link BeforeTransaction @BeforeTransaction}
+ * or {@link AfterTransaction @AfterTransaction}. As of Spring Framework 4.3,
+ * {@code @BeforeTransaction} and {@code @AfterTransaction} may also be declared
+ * on Java 8 based interface default methods.
*
* <h3>Configuring a Transaction Manager</h3>
* <p>{@code TransactionalTestExecutionListener} expects a
@@ -133,7 +135,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
@SuppressWarnings("deprecation")
private static final TransactionConfigurationAttributes defaultTxConfigAttributes = new TransactionConfigurationAttributes();
- protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource();
+ // Do not require @Transactional test methods to be public.
+ protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource(false);
@SuppressWarnings("deprecation")
private TransactionConfigurationAttributes configurationAttributes;
@@ -177,8 +180,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
transactionAttribute);
if (logger.isDebugEnabled()) {
- logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context "
- + testContext);
+ logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context " +
+ testContext);
}
if (transactionAttribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
@@ -189,8 +192,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
if (tm == null) {
throw new IllegalStateException(String.format(
- "Failed to retrieve PlatformTransactionManager for @Transactional test for test context %s.",
- testContext));
+ "Failed to retrieve PlatformTransactionManager for @Transactional test for test context %s.",
+ testContext));
}
}
@@ -246,12 +249,15 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
if (logger.isDebugEnabled()) {
logger.debug("Executing @BeforeTransaction method [" + method + "] for test context " + testContext);
}
+ ReflectionUtils.makeAccessible(method);
method.invoke(testContext.getTestInstance());
}
}
catch (InvocationTargetException ex) {
- logger.error("Exception encountered while executing @BeforeTransaction methods for test context "
- + testContext + ".", ex.getTargetException());
+ if (logger.isErrorEnabled()) {
+ logger.error("Exception encountered while executing @BeforeTransaction methods for test context " +
+ testContext + ".", ex.getTargetException());
+ }
ReflectionUtils.rethrowException(ex.getTargetException());
}
}
@@ -273,6 +279,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
if (logger.isDebugEnabled()) {
logger.debug("Executing @AfterTransaction method [" + method + "] for test context " + testContext);
}
+ ReflectionUtils.makeAccessible(method);
method.invoke(testContext.getTestInstance());
}
catch (InvocationTargetException ex) {
@@ -280,15 +287,15 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
if (afterTransactionException == null) {
afterTransactionException = targetException;
}
- logger.error("Exception encountered while executing @AfterTransaction method [" + method
- + "] for test context " + testContext, targetException);
+ logger.error("Exception encountered while executing @AfterTransaction method [" + method +
+ "] for test context " + testContext, targetException);
}
catch (Exception ex) {
if (afterTransactionException == null) {
afterTransactionException = ex;
}
- logger.error("Exception encountered while executing @AfterTransaction method [" + method
- + "] for test context " + testContext, ex);
+ logger.error("Exception encountered while executing @AfterTransaction method [" + method +
+ "] for test context " + testContext, ex);
}
}
@@ -311,20 +318,18 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* @see #getTransactionManager(TestContext)
*/
protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
- // look up by type and qualifier from @Transactional
+ // Look up by type and qualifier from @Transactional
if (StringUtils.hasText(qualifier)) {
try {
- // Use autowire-capable factory in order to support extended qualifier
- // matching (only exposed on the internal BeanFactory, not on the
- // ApplicationContext).
+ // Use autowire-capable factory in order to support extended qualifier matching
+ // (only exposed on the internal BeanFactory, not on the ApplicationContext).
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
}
catch (RuntimeException ex) {
if (logger.isWarnEnabled()) {
- logger.warn(
- String.format(
+ logger.warn(String.format(
"Caught exception while retrieving transaction manager with qualifier '%s' for test context %s",
qualifier, testContext), ex);
}
@@ -359,7 +364,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
/**
* Determine whether or not to rollback transactions by default for the
* supplied {@linkplain TestContext test context}.
- * <p>Supports {@link Rollback @Rollback} or
+ * <p>Supports {@link Rollback @Rollback}, {@link Commit @Commit}, or
* {@link TransactionConfiguration @TransactionConfiguration} at the
* class-level.
* @param testContext the test context for which the default rollback flag
@@ -370,7 +375,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
@SuppressWarnings("deprecation")
protected final boolean isDefaultRollback(TestContext testContext) throws Exception {
Class<?> testClass = testContext.getTestClass();
- Rollback rollback = findAnnotation(testClass, Rollback.class);
+ Rollback rollback = AnnotatedElementUtils.findMergedAnnotation(testClass, Rollback.class);
boolean rollbackPresent = (rollback != null);
TransactionConfigurationAttributes txConfigAttributes = retrieveConfigurationAttributes(testContext);
@@ -405,111 +410,45 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
*/
protected final boolean isRollback(TestContext testContext) throws Exception {
boolean rollback = isDefaultRollback(testContext);
- Rollback rollbackAnnotation = findAnnotation(testContext.getTestMethod(), Rollback.class);
+ Rollback rollbackAnnotation =
+ AnnotatedElementUtils.findMergedAnnotation(testContext.getTestMethod(), Rollback.class);
if (rollbackAnnotation != null) {
boolean rollbackOverride = rollbackAnnotation.value();
if (logger.isDebugEnabled()) {
logger.debug(String.format(
- "Method-level @Rollback(%s) overrides default rollback [%s] for test context %s.",
- rollbackOverride, rollback, testContext));
+ "Method-level @Rollback(%s) overrides default rollback [%s] for test context %s.",
+ rollbackOverride, rollback, testContext));
}
rollback = rollbackOverride;
}
else {
if (logger.isDebugEnabled()) {
logger.debug(String.format(
- "No method-level @Rollback override: using default rollback [%s] for test context %s.", rollback,
- testContext));
+ "No method-level @Rollback override: using default rollback [%s] for test context %s.",
+ rollback, testContext));
}
}
return rollback;
}
/**
- * Gets all superclasses of the supplied {@link Class class}, including the
- * class itself. The ordering of the returned list will begin with the
- * supplied class and continue up the class hierarchy, excluding {@link Object}.
- * <p>Note: This code has been borrowed from
- * {@link org.junit.internal.runners.TestClass#getSuperClasses(Class)} and
- * adapted.
- * @param clazz the class for which to retrieve the superclasses
- * @return all superclasses of the supplied class, excluding {@code Object}
- */
- private List<Class<?>> getSuperClasses(Class<?> clazz) {
- List<Class<?>> results = new ArrayList<Class<?>>();
- Class<?> current = clazz;
- while (current != null && Object.class != current) {
- results.add(current);
- current = current.getSuperclass();
- }
- return results;
- }
-
- /**
- * Gets all methods in the supplied {@link Class class} and its superclasses
+ * Get all methods in the supplied {@link Class class} and its superclasses
* which are annotated with the supplied {@code annotationType} but
* which are not <em>shadowed</em> by methods overridden in subclasses.
- * <p>Note: This code has been borrowed from
- * {@link org.junit.internal.runners.TestClass#getAnnotatedMethods(Class)}
- * and adapted.
+ * <p>Default methods on interfaces are also detected.
* @param clazz the class for which to retrieve the annotated methods
* @param annotationType the annotation type for which to search
* @return all annotated methods in the supplied class and its superclasses
+ * as well as annotated interface default methods
*/
private List<Method> getAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {
- List<Method> results = new ArrayList<Method>();
- for (Class<?> current : getSuperClasses(clazz)) {
- for (Method method : current.getDeclaredMethods()) {
- Annotation annotation = getAnnotation(method, annotationType);
- if (annotation != null && !isShadowed(method, results)) {
- results.add(method);
- }
+ List<Method> methods = new ArrayList<Method>(4);
+ for (Method method : ReflectionUtils.getUniqueDeclaredMethods(clazz)) {
+ if (AnnotationUtils.getAnnotation(method, annotationType) != null) {
+ methods.add(method);
}
}
- return results;
- }
-
- /**
- * Determine if the supplied {@link Method method} is <em>shadowed</em> by
- * a method in the supplied {@link List list} of previous methods.
- * <p>Note: This code has been borrowed from
- * {@link org.junit.internal.runners.TestClass#isShadowed(Method, List)}.
- * @param method the method to check for shadowing
- * @param previousMethods the list of methods which have previously been processed
- * @return {@code true} if the supplied method is shadowed by a
- * method in the {@code previousMethods} list
- */
- private boolean isShadowed(Method method, List<Method> previousMethods) {
- for (Method each : previousMethods) {
- if (isShadowed(method, each)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Determine if the supplied {@linkplain Method current method} is
- * <em>shadowed</em> by a {@linkplain Method previous method}.
- * <p>Note: This code has been borrowed from
- * {@link org.junit.internal.runners.TestClass#isShadowed(Method, Method)}.
- * @param current the current method
- * @param previous the previous method
- * @return {@code true} if the previous method shadows the current one
- */
- private boolean isShadowed(Method current, Method previous) {
- if (!previous.getName().equals(current.getName())) {
- return false;
- }
- if (previous.getParameterTypes().length != current.getParameterTypes().length) {
- return false;
- }
- for (int i = 0; i < previous.getParameterTypes().length; i++) {
- if (!previous.getParameterTypes()[i].equals(current.getParameterTypes()[i])) {
- return false;
- }
- }
- return true;
+ return methods;
}
/**
@@ -531,19 +470,18 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
if (this.configurationAttributes == null) {
Class<?> clazz = testContext.getTestClass();
- TransactionConfiguration txConfig = AnnotatedElementUtils.findMergedAnnotation(clazz,
- TransactionConfiguration.class);
+ TransactionConfiguration txConfig =
+ AnnotatedElementUtils.findMergedAnnotation(clazz, TransactionConfiguration.class);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Retrieved @TransactionConfiguration [%s] for test class [%s].",
- txConfig, clazz.getName()));
+ txConfig, clazz.getName()));
}
- TransactionConfigurationAttributes configAttributes = (txConfig == null ? defaultTxConfigAttributes
- : new TransactionConfigurationAttributes(txConfig.transactionManager(), txConfig.defaultRollback()));
-
+ TransactionConfigurationAttributes configAttributes = (txConfig == null ? defaultTxConfigAttributes :
+ new TransactionConfigurationAttributes(txConfig.transactionManager(), txConfig.defaultRollback()));
if (logger.isDebugEnabled()) {
logger.debug(String.format("Using TransactionConfigurationAttributes %s for test class [%s].",
- configAttributes, clazz.getName()));
+ configAttributes, clazz.getName()));
}
this.configurationAttributes = configAttributes;
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java
index f6e1ac9e..a74ca6a2 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -53,6 +53,7 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
* {@link #loadBeanDefinitions}.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 3.2
* @see #loadContext(MergedContextConfiguration)
* @see #loadContext(String...)
@@ -256,15 +257,17 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
* loader <i>after</i> bean definitions have been loaded into the context but
* <i>before</i> the context is refreshed.
*
- * <p>The default implementation is empty but can be overridden in subclasses
- * to customize the web application context.
+ * <p>The default implementation simply delegates to
+ * {@link AbstractContextLoader#customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)}.
*
* @param context the newly created web application context
* @param webMergedConfig the merged context configuration to use to load the
* web application context
* @see #loadContext(MergedContextConfiguration)
+ * @see #customizeContext(ConfigurableApplicationContext, MergedContextConfiguration)
*/
protected void customizeContext(GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) {
+ super.customizeContext(context, webMergedConfig);
}
// --- ContextLoader -------------------------------------------------------
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java
index a1bdb5a2..32e79eb5 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -25,7 +25,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Conventions;
-import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
@@ -33,7 +33,6 @@ import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
-import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
@@ -58,9 +57,10 @@ import org.springframework.web.context.request.ServletWebRequest;
* <p>Note that {@code ServletTestExecutionListener} is enabled by default but
* generally takes no action if the {@linkplain TestContext#getTestClass() test
* class} is not annotated with {@link WebAppConfiguration @WebAppConfiguration}.
- * See the Javadoc for individual methods in this class for details.
+ * See the javadocs for individual methods in this class for details.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 3.2
*/
public class ServletTestExecutionListener extends AbstractTestExecutionListener {
@@ -70,33 +70,42 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
* whether or not the {@code ServletTestExecutionListener} should {@linkplain
* RequestContextHolder#resetRequestAttributes() reset} Spring Web's
* {@code RequestContextHolder} in {@link #afterTestMethod(TestContext)}.
- *
* <p>Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}.
*/
public static final String RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE = Conventions.getQualifiedAttributeName(
- ServletTestExecutionListener.class, "resetRequestContextHolder");
+ ServletTestExecutionListener.class, "resetRequestContextHolder");
/**
* Attribute name for a {@link TestContext} attribute which indicates that
* {@code ServletTestExecutionListener} has already populated Spring Web's
* {@code RequestContextHolder}.
- *
* <p>Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}.
*/
public static final String POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE = Conventions.getQualifiedAttributeName(
- ServletTestExecutionListener.class, "populatedRequestContextHolder");
+ ServletTestExecutionListener.class, "populatedRequestContextHolder");
/**
* Attribute name for a request attribute which indicates that the
* {@link MockHttpServletRequest} stored in the {@link RequestAttributes}
* in Spring Web's {@link RequestContextHolder} was created by the TestContext
* framework.
- *
* <p>Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}.
* @since 4.2
*/
public static final String CREATED_BY_THE_TESTCONTEXT_FRAMEWORK = Conventions.getQualifiedAttributeName(
- ServletTestExecutionListener.class, "createdByTheTestContextFramework");
+ ServletTestExecutionListener.class, "createdByTheTestContextFramework");
+
+ /**
+ * Attribute name for a {@link TestContext} attribute which indicates that that
+ * the {@code ServletTestExecutionListener} should be activated. When not set to
+ * {@code true}, activation occurs when the {@linkplain TestContext#getTestClass()
+ * test class} is annotated with {@link WebAppConfiguration @WebAppConfiguration}.
+ * <p>Permissible values include {@link Boolean#TRUE} and {@link Boolean#FALSE}.
+ * @since 4.3
+ */
+ public static final String ACTIVATE_LISTENER = Conventions.getQualifiedAttributeName(
+ ServletTestExecutionListener.class, "activateListener");
+
private static final Log logger = LogFactory.getLog(ServletTestExecutionListener.class);
@@ -114,7 +123,6 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
* callback phase via Spring Web's {@link RequestContextHolder}, but only if
* the {@linkplain TestContext#getTestClass() test class} is annotated with
* {@link WebAppConfiguration @WebAppConfiguration}.
- *
* @see TestExecutionListener#prepareTestInstance(TestContext)
* @see #setUpRequestContextIfNecessary(TestContext)
*/
@@ -128,7 +136,6 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
* {@link RequestContextHolder}, but only if the
* {@linkplain TestContext#getTestClass() test class} is annotated with
* {@link WebAppConfiguration @WebAppConfiguration}.
- *
* @see TestExecutionListener#beforeTestMethod(TestContext)
* @see #setUpRequestContextIfNecessary(TestContext)
*/
@@ -146,11 +153,9 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
* into the test instance for subsequent tests by setting the
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
* in the test context to {@code true}.
- *
* <p>The {@link #RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE} and
* {@link #POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE} will be subsequently
* removed from the test context, regardless of their values.
- *
* @see TestExecutionListener#afterTestMethod(TestContext)
*/
@Override
@@ -167,8 +172,9 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
testContext.removeAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE);
}
- private boolean notAnnotatedWithWebAppConfiguration(TestContext testContext) {
- return AnnotationUtils.findAnnotation(testContext.getTestClass(), WebAppConfiguration.class) == null;
+ private boolean isActivated(TestContext testContext) {
+ return (Boolean.TRUE.equals(testContext.getAttribute(ACTIVATE_LISTENER)) ||
+ AnnotatedElementUtils.hasAnnotation(testContext.getTestClass(), WebAppConfiguration.class));
}
private boolean alreadyPopulatedRequestContextHolder(TestContext testContext) {
@@ -176,7 +182,7 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
}
private void setUpRequestContextIfNecessary(TestContext testContext) {
- if (notAnnotatedWithWebAppConfiguration(testContext) || alreadyPopulatedRequestContextHolder(testContext)) {
+ if (!isActivated(testContext) || alreadyPopulatedRequestContextHolder(testContext)) {
return;
}
@@ -185,14 +191,16 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
if (context instanceof WebApplicationContext) {
WebApplicationContext wac = (WebApplicationContext) context;
ServletContext servletContext = wac.getServletContext();
- Assert.state(servletContext instanceof MockServletContext, String.format(
- "The WebApplicationContext for test context %s must be configured with a MockServletContext.",
- testContext));
+ if (!(servletContext instanceof MockServletContext)) {
+ throw new IllegalStateException(String.format(
+ "The WebApplicationContext for test context %s must be configured with a MockServletContext.",
+ testContext));
+ }
if (logger.isDebugEnabled()) {
logger.debug(String.format(
- "Setting up MockHttpServletRequest, MockHttpServletResponse, ServletWebRequest, and RequestContextHolder for test context %s.",
- testContext));
+ "Setting up MockHttpServletRequest, MockHttpServletResponse, ServletWebRequest, and RequestContextHolder for test context %s.",
+ testContext));
}
MockServletContext mockServletContext = (MockServletContext) servletContext;
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java
index e9d17610..695d5f5a 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java
@@ -23,8 +23,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import org.springframework.test.context.BootstrapWith;
-
/**
* {@code @WebAppConfiguration} is a class-level annotation that is used to
* declare that the {@code ApplicationContext} loaded for an integration test
@@ -53,7 +51,6 @@ import org.springframework.test.context.BootstrapWith;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@BootstrapWith(WebTestContextBootstrapper.class)
public @interface WebAppConfiguration {
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
index cd1187b5..98bbdad2 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
@@ -22,6 +22,7 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.style.ToStringCreator;
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
+import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.ObjectUtils;
@@ -132,13 +133,48 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
String resourceBasePath, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
+ this(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations,
+ propertySourceProperties, null, resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate, parent);
+ }
+
+ /**
+ * Create a new {@code WebMergedContextConfiguration} instance for the
+ * supplied parameters.
+ * <p>If a {@code null} value is supplied for {@code locations},
+ * {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
+ * or {@code propertySourceProperties} an empty array will be stored instead.
+ * If a {@code null} value is supplied for {@code contextInitializerClasses}
+ * or {@code contextCustomizers}, an empty set will be stored instead.
+ * If an <em>empty</em> value is supplied for the {@code resourceBasePath}
+ * an empty string will be used. Furthermore, active profiles will be sorted,
+ * and duplicate profiles will be removed.
+ * @param testClass the test class for which the configuration was merged
+ * @param locations the merged context resource locations
+ * @param classes the merged annotated classes
+ * @param contextInitializerClasses the merged context initializer classes
+ * @param activeProfiles the merged active bean definition profiles
+ * @param propertySourceLocations the merged {@code PropertySource} locations
+ * @param propertySourceProperties the merged {@code PropertySource} properties
+ * @param contextCustomizers the context customizers
+ * @param resourceBasePath the resource path to the root directory of the web application
+ * @param contextLoader the resolved {@code ContextLoader}
+ * @param cacheAwareContextLoaderDelegate a cache-aware context loader
+ * delegate with which to retrieve the parent context
+ * @param parent the parent configuration or {@code null} if there is no parent
+ * @since 4.3
+ */
+ public WebMergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
+ Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
+ String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
+ Set<ContextCustomizer> contextCustomizers, String resourceBasePath, ContextLoader contextLoader,
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
+
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations,
- propertySourceProperties, contextLoader, cacheAwareContextLoaderDelegate, parent);
+ propertySourceProperties, contextCustomizers, contextLoader, cacheAwareContextLoaderDelegate, parent);
- this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
+ this.resourceBasePath = (StringUtils.hasText(resourceBasePath) ? resourceBasePath : "");
}
-
/**
* Get the resource path to the root directory of the web application for the
* {@linkplain #getTestClass() test class}, configured via {@code @WebAppConfiguration}.
@@ -182,6 +218,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
* {@linkplain #getActiveProfiles() active profiles},
* {@linkplain #getPropertySourceLocations() property source locations},
* {@linkplain #getPropertySourceProperties() property source properties},
+ * {@linkplain #getContextCustomizers() context customizers},
* {@linkplain #getResourceBasePath() resource base path}, the name of the
* {@link #getContextLoader() ContextLoader}, and the
* {@linkplain #getParent() parent configuration}.
@@ -196,6 +233,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))
.append("propertySourceLocations", ObjectUtils.nullSafeToString(getPropertySourceLocations()))
.append("propertySourceProperties", ObjectUtils.nullSafeToString(getPropertySourceProperties()))
+ .append("contextCustomizers", getContextCustomizers())
.append("resourceBasePath", getResourceBasePath())
.append("contextLoader", nullSafeToString(getContextLoader()))
.append("parent", getParent())
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
index 94741f5a..f779450c 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,7 +16,7 @@
package org.springframework.test.context.web;
-import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestContextBootstrapper;
@@ -45,12 +45,12 @@ public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper {
*/
@Override
protected Class<? extends ContextLoader> getDefaultContextLoaderClass(Class<?> testClass) {
- if (AnnotationUtils.findAnnotation(testClass, WebAppConfiguration.class) != null) {
+ if (AnnotatedElementUtils.findMergedAnnotation(testClass, WebAppConfiguration.class) != null) {
return WebDelegatingSmartContextLoader.class;
}
-
- // else...
- return super.getDefaultContextLoaderClass(testClass);
+ else {
+ return super.getDefaultContextLoaderClass(testClass);
+ }
}
/**
@@ -61,14 +61,14 @@ public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper {
*/
@Override
protected MergedContextConfiguration processMergedContextConfiguration(MergedContextConfiguration mergedConfig) {
- WebAppConfiguration webAppConfiguration = AnnotationUtils.findAnnotation(mergedConfig.getTestClass(),
- WebAppConfiguration.class);
+ WebAppConfiguration webAppConfiguration =
+ AnnotatedElementUtils.findMergedAnnotation(mergedConfig.getTestClass(), WebAppConfiguration.class);
if (webAppConfiguration != null) {
return new WebMergedContextConfiguration(mergedConfig, webAppConfiguration.value());
}
-
- // else...
- return mergedConfig;
+ else {
+ return mergedConfig;
+ }
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java
new file mode 100644
index 00000000..bd7c7257
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2002-2016 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.context.web.socket;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Set;
+import javax.websocket.ClientEndpointConfig;
+import javax.websocket.DeploymentException;
+import javax.websocket.Endpoint;
+import javax.websocket.Extension;
+import javax.websocket.Session;
+import javax.websocket.server.ServerContainer;
+import javax.websocket.server.ServerEndpointConfig;
+
+/**
+ * Mock implementation of the {@link javax.websocket.server.ServerContainer} interface.
+ *
+ * @author Sam Brannen
+ * @since 4.3.1
+ */
+class MockServerContainer implements ServerContainer {
+
+ private long defaultAsyncSendTimeout;
+
+ private long defaultMaxSessionIdleTimeout;
+
+ private int defaultMaxBinaryMessageBufferSize;
+
+ private int defaultMaxTextMessageBufferSize;
+
+
+ // --- WebSocketContainer --------------------------------------------------
+
+ @Override
+ public long getDefaultAsyncSendTimeout() {
+ return this.defaultAsyncSendTimeout;
+ }
+
+ @Override
+ public void setAsyncSendTimeout(long timeout) {
+ this.defaultAsyncSendTimeout = timeout;
+ }
+
+ @Override
+ public long getDefaultMaxSessionIdleTimeout() {
+ return this.defaultMaxSessionIdleTimeout;
+ }
+
+ @Override
+ public void setDefaultMaxSessionIdleTimeout(long timeout) {
+ this.defaultMaxSessionIdleTimeout = timeout;
+ }
+
+ @Override
+ public int getDefaultMaxBinaryMessageBufferSize() {
+ return this.defaultMaxBinaryMessageBufferSize;
+ }
+
+ @Override
+ public void setDefaultMaxBinaryMessageBufferSize(int max) {
+ this.defaultMaxBinaryMessageBufferSize = max;
+ }
+
+ @Override
+ public int getDefaultMaxTextMessageBufferSize() {
+ return this.defaultMaxTextMessageBufferSize;
+ }
+
+ @Override
+ public void setDefaultMaxTextMessageBufferSize(int max) {
+ this.defaultMaxTextMessageBufferSize = max;
+ }
+
+ @Override
+ public Set<Extension> getInstalledExtensions() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Session connectToServer(Object annotatedEndpointInstance, URI path) throws DeploymentException, IOException {
+ throw new UnsupportedOperationException("MockServerContainer does not support connectToServer(Object, URI)");
+ }
+
+ @Override
+ public Session connectToServer(Class<?> annotatedEndpointClass, URI path) throws DeploymentException, IOException {
+ throw new UnsupportedOperationException("MockServerContainer does not support connectToServer(Class, URI)");
+ }
+
+ @Override
+ public Session connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path)
+ throws DeploymentException, IOException {
+
+ throw new UnsupportedOperationException(
+ "MockServerContainer does not support connectToServer(Endpoint, ClientEndpointConfig, URI)");
+ }
+
+ @Override
+ public Session connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig cec, URI path)
+ throws DeploymentException, IOException {
+
+ throw new UnsupportedOperationException(
+ "MockServerContainer does not support connectToServer(Class, ClientEndpointConfig, URI)");
+ }
+
+
+ // --- ServerContainer -----------------------------------------------------
+
+ @Override
+ public void addEndpoint(Class<?> endpointClass) throws DeploymentException {
+ throw new UnsupportedOperationException("MockServerContainer does not support addEndpoint(Class)");
+ }
+
+ @Override
+ public void addEndpoint(ServerEndpointConfig serverConfig) throws DeploymentException {
+ throw new UnsupportedOperationException(
+ "MockServerContainer does not support addEndpoint(ServerEndpointConfig)");
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizer.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizer.java
new file mode 100644
index 00000000..e68914ee
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2016 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.context.web.socket;
+
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.test.context.ContextCustomizer;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * {@link ContextCustomizer} that instantiates a new {@link MockServerContainer}
+ * and stores it in the {@code ServletContext} under the attribute named
+ * {@code "javax.websocket.server.ServerContainer"}.
+ *
+ * @author Sam Brannen
+ * @since 4.3.1
+ */
+class MockServerContainerContextCustomizer implements ContextCustomizer {
+
+ @Override
+ public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
+ if (context instanceof WebApplicationContext) {
+ WebApplicationContext wac = (WebApplicationContext) context;
+ wac.getServletContext().setAttribute("javax.websocket.server.ServerContainer", new MockServerContainer());
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (this == other || (other != null && getClass() == other.getClass()));
+ }
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizerFactory.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizerFactory.java
new file mode 100644
index 00000000..c76959c3
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizerFactory.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2002-2016 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.context.web.socket;
+
+import java.util.List;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.test.context.ContextConfigurationAttributes;
+import org.springframework.test.context.ContextCustomizer;
+import org.springframework.test.context.ContextCustomizerFactory;
+import org.springframework.util.ClassUtils;
+
+/**
+ * {@link ContextCustomizerFactory} which creates a {@link MockServerContainerContextCustomizer}
+ * if WebSocket support is present in the classpath and the test class is annotated
+ * with {@code @WebAppConfiguration}.
+ *
+ * @author Sam Brannen
+ * @since 4.3.1
+ */
+class MockServerContainerContextCustomizerFactory implements ContextCustomizerFactory {
+
+ private static final String WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME =
+ "org.springframework.test.context.web.WebAppConfiguration";
+
+ private static final String MOCK_SERVER_CONTAINER_CONTEXT_CUSTOMIZER_CLASS_NAME =
+ "org.springframework.test.context.web.socket.MockServerContainerContextCustomizer";
+
+ private static final boolean webSocketPresent = ClassUtils.isPresent("javax.websocket.server.ServerContainer",
+ MockServerContainerContextCustomizerFactory.class.getClassLoader());
+
+
+ @Override
+ public ContextCustomizer createContextCustomizer(Class<?> testClass,
+ List<ContextConfigurationAttributes> configAttributes) {
+
+ if (webSocketPresent && isAnnotatedWithWebAppConfiguration(testClass)) {
+ try {
+ Class<?> clazz = ClassUtils.forName(MOCK_SERVER_CONTAINER_CONTEXT_CUSTOMIZER_CLASS_NAME,
+ getClass().getClassLoader());
+ return (ContextCustomizer) BeanUtils.instantiateClass(clazz);
+ }
+ catch (Throwable ex) {
+ throw new IllegalStateException("Failed to enable WebSocket test support; could not load class: " +
+ MOCK_SERVER_CONTAINER_CONTEXT_CUSTOMIZER_CLASS_NAME, ex);
+ }
+ }
+
+ // Else, nothing to customize
+ return null;
+ }
+
+ private static boolean isAnnotatedWithWebAppConfiguration(Class<?> testClass) {
+ return (AnnotatedElementUtils.findMergedAnnotationAttributes(testClass,
+ WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME, false, false) != null);
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/package-info.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/package-info.java
new file mode 100644
index 00000000..7b97278f
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * WebSocket support classes for the <em>Spring TestContext Framework</em>.
+ */
+package org.springframework.test.context.web.socket;
diff --git a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java
index 8e3e9a05..e0e1bc0e 100644
--- a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -124,6 +124,7 @@ public class JdbcTestUtils {
*/
public static int deleteFromTableWhere(JdbcTemplate jdbcTemplate, String tableName, String whereClause,
Object... args) {
+
String sql = "DELETE FROM " + tableName;
if (StringUtils.hasText(whereClause)) {
sql += " WHERE " + whereClause;
@@ -170,6 +171,7 @@ public class JdbcTestUtils {
@Deprecated
public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader resourceLoader,
String sqlResourcePath, boolean continueOnError) throws DataAccessException {
+
Resource resource = resourceLoader.getResource(sqlResourcePath);
executeSqlScript(jdbcTemplate, resource, continueOnError);
}
@@ -197,6 +199,7 @@ public class JdbcTestUtils {
@Deprecated
public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource, boolean continueOnError)
throws DataAccessException {
+
executeSqlScript(jdbcTemplate, new EncodedResource(resource), continueOnError);
}
@@ -220,6 +223,7 @@ public class JdbcTestUtils {
@Deprecated
public static void executeSqlScript(JdbcTemplate jdbcTemplate, EncodedResource resource, boolean continueOnError)
throws DataAccessException {
+
new ResourceDatabasePopulator(continueOnError, false, resource.getEncoding(), resource.getResource()).execute(jdbcTemplate.getDataSource());
}
@@ -288,4 +292,5 @@ public class JdbcTestUtils {
public static void splitSqlScript(String script, char delim, List<String> statements) {
ScriptUtils.splitSqlScript(script, delim, statements);
}
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java b/spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java
index 4eda8a45..7660ee2c 100644
--- a/spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -32,6 +32,7 @@ import org.springframework.util.Assert;
* @since 4.2
* @see org.springframework.aop.support.AopUtils
* @see org.springframework.aop.framework.AopProxyUtils
+ * @see ReflectionTestUtils
*/
public class AopTestUtils {
diff --git a/spring-test/src/main/java/org/springframework/test/util/AssertionErrors.java b/spring-test/src/main/java/org/springframework/test/util/AssertionErrors.java
index 0302686b..f07574e8 100644
--- a/spring-test/src/main/java/org/springframework/test/util/AssertionErrors.java
+++ b/spring-test/src/main/java/org/springframework/test/util/AssertionErrors.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.test.util;
import org.springframework.util.ObjectUtils;
@@ -26,13 +27,8 @@ import org.springframework.util.ObjectUtils;
*/
public abstract class AssertionErrors {
-
- private AssertionErrors() {
- }
-
/**
* Fails a test with the given message.
- *
* @param message describes the reason for the failure
*/
public static void fail(String message) {
@@ -42,7 +38,6 @@ public abstract class AssertionErrors {
/**
* Fails a test with the given message passing along expected and actual
* values to be added to the message.
- *
* <p>For example given:
* <pre class="code">
* assertEquals("Response header [" + name + "]", actual, expected);
@@ -51,7 +46,6 @@ public abstract class AssertionErrors {
* <pre class="code">
* Response header [Accept] expected:&lt;application/json&gt; but was:&lt;text/plain&gt;
* </pre>
- *
* @param message describes the value that failed the match
* @param expected expected value
* @param actual actual value
@@ -63,7 +57,6 @@ public abstract class AssertionErrors {
/**
* Assert the given condition is {@code true} and raise an
* {@link AssertionError} if it is not.
- *
* @param message the message
* @param condition the condition to test for
*/
@@ -79,14 +72,13 @@ public abstract class AssertionErrors {
* <pre class="code">
* assertEquals("Response header [" + name + "]", actual, expected);
* </pre>
- *
* @param message describes the value being checked
* @param expected the expected value
* @param actual the actual value
*/
public static void assertEquals(String message, Object expected, Object actual) {
if (!ObjectUtils.nullSafeEquals(expected, actual)) {
- fail(message, expected, actual);
+ fail(message, ObjectUtils.nullSafeToString(expected), ObjectUtils.nullSafeToString(actual));
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java b/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java
index a82b8a40..cbb74dc6 100644
--- a/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java
+++ b/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java
@@ -16,27 +16,21 @@
package org.springframework.test.util;
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.List;
import java.util.Map;
+import com.jayway.jsonpath.InvalidPathException;
+import com.jayway.jsonpath.JsonPath;
import org.hamcrest.Matcher;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
-import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
-import com.jayway.jsonpath.InvalidPathException;
-import com.jayway.jsonpath.JsonPath;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.IsInstanceOf.instanceOf;
-import static org.springframework.test.util.AssertionErrors.assertEquals;
-import static org.springframework.test.util.AssertionErrors.assertTrue;
-import static org.springframework.test.util.AssertionErrors.fail;
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.core.IsInstanceOf.*;
+import static org.springframework.test.util.AssertionErrors.*;
/**
* A helper class for applying assertions via JSON path expressions.
@@ -52,26 +46,6 @@ import static org.springframework.test.util.AssertionErrors.fail;
*/
public class JsonPathExpectationsHelper {
- private static Method compileMethod;
-
- private static Object emptyFilters;
-
- static {
- // Reflective bridging between JsonPath 0.9.x and 1.x
- for (Method candidate : JsonPath.class.getMethods()) {
- if (candidate.getName().equals("compile")) {
- Class<?>[] paramTypes = candidate.getParameterTypes();
- if (paramTypes.length == 2 && String.class == paramTypes[0] && paramTypes[1].isArray()) {
- compileMethod = candidate;
- emptyFilters = Array.newInstance(paramTypes[1].getComponentType(), 0);
- break;
- }
- }
- }
- Assert.state(compileMethod != null, "Unexpected JsonPath API - no compile(String, ...) method found");
- }
-
-
private final String expression;
private final JsonPath jsonPath;
@@ -86,8 +60,7 @@ public class JsonPathExpectationsHelper {
public JsonPathExpectationsHelper(String expression, Object... args) {
Assert.hasText(expression, "expression must not be null or empty");
this.expression = String.format(expression, args);
- this.jsonPath = (JsonPath) ReflectionUtils.invokeMethod(
- compileMethod, null, this.expression, emptyFilters);
+ this.jsonPath = JsonPath.compile(this.expression);
}
diff --git a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java
index 913986a4..9c203d74 100644
--- a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java
@@ -57,8 +57,8 @@ public abstract class MetaAnnotationUtils {
/**
* Find the {@link AnnotationDescriptor} for the supplied {@code annotationType}
- * on the supplied {@link Class}, traversing its annotations and superclasses
- * if no annotation can be found on the given class itself.
+ * on the supplied {@link Class}, traversing its annotations, interfaces, and
+ * superclasses if no annotation can be found on the given class itself.
* <p>This method explicitly handles class-level annotations which are not
* declared as {@linkplain java.lang.annotation.Inherited inherited} <em>as
* well as meta-annotations</em>.
@@ -67,14 +67,12 @@ public abstract class MetaAnnotationUtils {
* <li>Search for the annotation on the given class and return a corresponding
* {@code AnnotationDescriptor} if found.
* <li>Recursively search through all annotations that the given class declares.
+ * <li>Recursively search through all interfaces implemented by the given class.
* <li>Recursively search through the superclass hierarchy of the given class.
* </ol>
* <p>In this context, the term <em>recursively</em> means that the search
- * process continues by returning to step #1 with the current annotation or
- * superclass as the class to look for annotations on.
- * <p>If the supplied {@code clazz} is an interface, only the interface
- * itself will be checked; the inheritance hierarchy for interfaces will not
- * be traversed.
+ * process continues by returning to step #1 with the current annotation,
+ * interface, or superclass as the class to look for annotations on.
* @param clazz the class to look for annotations on
* @param annotationType the type of annotation to look for
* @return the corresponding annotation descriptor if the annotation was found;
@@ -123,6 +121,15 @@ public abstract class MetaAnnotationUtils {
}
}
+ // Declared on interface?
+ for (Class<?> ifc : clazz.getInterfaces()) {
+ AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(ifc, visited, annotationType);
+ if (descriptor != null) {
+ return new AnnotationDescriptor<T>(clazz, descriptor.getDeclaringClass(),
+ descriptor.getComposedAnnotation(), descriptor.getAnnotation());
+ }
+ }
+
// Declared on a superclass?
return findAnnotationDescriptor(clazz.getSuperclass(), visited, annotationType);
}
@@ -132,8 +139,9 @@ public abstract class MetaAnnotationUtils {
* in the inheritance hierarchy of the specified {@code clazz} (including
* the specified {@code clazz} itself) which declares at least one of the
* specified {@code annotationTypes}.
- * <p>This method traverses the annotations and superclasses of the specified
- * {@code clazz} if no annotation can be found on the given class itself.
+ * <p>This method traverses the annotations, interfaces, and superclasses
+ * of the specified {@code clazz} if no annotation can be found on the given
+ * class itself.
* <p>This method explicitly handles class-level annotations which are not
* declared as {@linkplain java.lang.annotation.Inherited inherited} <em>as
* well as meta-annotations</em>.
@@ -143,14 +151,12 @@ public abstract class MetaAnnotationUtils {
* the given class and return a corresponding {@code UntypedAnnotationDescriptor}
* if found.
* <li>Recursively search through all annotations that the given class declares.
+ * <li>Recursively search through all interfaces implemented by the given class.
* <li>Recursively search through the superclass hierarchy of the given class.
* </ol>
* <p>In this context, the term <em>recursively</em> means that the search
- * process continues by returning to step #1 with the current annotation or
- * superclass as the class to look for annotations on.
- * <p>If the supplied {@code clazz} is an interface, only the interface
- * itself will be checked; the inheritance hierarchy for interfaces will not
- * be traversed.
+ * process continues by returning to step #1 with the current annotation,
+ * interface, or superclass as the class to look for annotations on.
* @param clazz the class to look for annotations on
* @param annotationTypes the types of annotations to look for
* @return the corresponding annotation descriptor if one of the annotations
@@ -203,6 +209,15 @@ public abstract class MetaAnnotationUtils {
}
}
+ // Declared on interface?
+ for (Class<?> ifc : clazz.getInterfaces()) {
+ UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(ifc, visited, annotationTypes);
+ if (descriptor != null) {
+ return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(),
+ descriptor.getComposedAnnotation(), descriptor.getAnnotation());
+ }
+ }
+
// Declared on a superclass?
return findAnnotationDescriptorForTypes(clazz.getSuperclass(), visited, annotationTypes);
}
diff --git a/spring-test/src/main/java/org/springframework/test/util/ReflectionTestUtils.java b/spring-test/src/main/java/org/springframework/test/util/ReflectionTestUtils.java
index f40227fb..4f508511 100644
--- a/spring-test/src/main/java/org/springframework/test/util/ReflectionTestUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/util/ReflectionTestUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -60,6 +60,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @since 2.5
* @see ReflectionUtils
+ * @see AopTestUtils
*/
public class ReflectionTestUtils {
@@ -137,6 +138,9 @@ public class ReflectionTestUtils {
* Set the {@linkplain Field field} with the given {@code name}/{@code type}
* on the provided {@code targetObject}/{@code targetClass} to the supplied
* {@code value}.
+ * <p>If the supplied {@code targetObject} is a <em>proxy</em>, it will
+ * be {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing
+ * the field to be set on the ultimate target of the proxy.
* <p>This method traverses the class hierarchy in search of the desired
* field. In addition, an attempt will be made to make non-{@code public}
* fields <em>accessible</em>, thus allowing one to set {@code protected},
@@ -150,33 +154,36 @@ public class ReflectionTestUtils {
* @param value the value to set
* @param type the type of the field to set; may be {@code null} if
* {@code name} is specified
+ * @since 4.2
* @see ReflectionUtils#findField(Class, String, Class)
* @see ReflectionUtils#makeAccessible(Field)
* @see ReflectionUtils#setField(Field, Object, Object)
- * @since 4.2
+ * @see AopTestUtils#getUltimateTargetObject(Object)
*/
public static void setField(Object targetObject, Class<?> targetClass, String name, Object value, Class<?> type) {
Assert.isTrue(targetObject != null || targetClass != null,
"Either targetObject or targetClass for the field must be specified");
+ Object ultimateTarget = (targetObject != null ? AopTestUtils.getUltimateTargetObject(targetObject) : null);
+
if (targetClass == null) {
- targetClass = targetObject.getClass();
+ targetClass = ultimateTarget.getClass();
}
Field field = ReflectionUtils.findField(targetClass, name, type);
if (field == null) {
throw new IllegalArgumentException(String.format(
- "Could not find field '%s' of type [%s] on target object [%s] or target class [%s]", name, type,
- targetObject, targetClass));
+ "Could not find field '%s' of type [%s] on %s or target class [%s]", name, type,
+ safeToString(ultimateTarget), targetClass));
}
if (logger.isDebugEnabled()) {
logger.debug(String.format(
- "Setting field '%s' of type [%s] on target object [%s] or target class [%s] to value [%s]", name, type,
- targetObject, targetClass, value));
+ "Setting field '%s' of type [%s] on %s or target class [%s] to value [%s]", name, type,
+ safeToString(ultimateTarget), targetClass, value));
}
ReflectionUtils.makeAccessible(field);
- ReflectionUtils.setField(field, targetObject, value);
+ ReflectionUtils.setField(field, ultimateTarget, value);
}
/**
@@ -213,6 +220,9 @@ public class ReflectionTestUtils {
/**
* Get the value of the {@linkplain Field field} with the given {@code name}
* from the provided {@code targetObject}/{@code targetClass}.
+ * <p>If the supplied {@code targetObject} is a <em>proxy</em>, it will
+ * be {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing
+ * the field to be retrieved from the ultimate target of the proxy.
* <p>This method traverses the class hierarchy in search of the desired
* field. In addition, an attempt will be made to make non-{@code public}
* fields <em>accessible</em>, thus allowing one to get {@code protected},
@@ -229,28 +239,30 @@ public class ReflectionTestUtils {
* @see ReflectionUtils#findField(Class, String, Class)
* @see ReflectionUtils#makeAccessible(Field)
* @see ReflectionUtils#getField(Field, Object)
+ * @see AopTestUtils#getUltimateTargetObject(Object)
*/
public static Object getField(Object targetObject, Class<?> targetClass, String name) {
Assert.isTrue(targetObject != null || targetClass != null,
"Either targetObject or targetClass for the field must be specified");
+ Object ultimateTarget = (targetObject != null ? AopTestUtils.getUltimateTargetObject(targetObject) : null);
+
if (targetClass == null) {
- targetClass = targetObject.getClass();
+ targetClass = ultimateTarget.getClass();
}
Field field = ReflectionUtils.findField(targetClass, name);
if (field == null) {
- throw new IllegalArgumentException(
- String.format("Could not find field '%s' on target object [%s] or target class [%s]", name,
- targetObject, targetClass));
+ throw new IllegalArgumentException(String.format("Could not find field '%s' on %s or target class [%s]",
+ name, safeToString(ultimateTarget), targetClass));
}
if (logger.isDebugEnabled()) {
- logger.debug(String.format("Getting field '%s' from target object [%s] or target class [%s]", name,
- targetObject, targetClass));
+ logger.debug(String.format("Getting field '%s' from %s or target class [%s]", name,
+ safeToString(ultimateTarget), targetClass));
}
ReflectionUtils.makeAccessible(field);
- return ReflectionUtils.getField(field, targetObject);
+ return ReflectionUtils.getField(field, ultimateTarget);
}
/**
@@ -314,13 +326,16 @@ public class ReflectionTestUtils {
method = ReflectionUtils.findMethod(target.getClass(), setterMethodName, paramTypes);
}
if (method == null) {
- throw new IllegalArgumentException("Could not find setter method '" + setterMethodName +
- "' on target [" + target + "] with parameter type [" + type + "]");
+ throw new IllegalArgumentException(String.format(
+ "Could not find setter method '%s' on %s with parameter type [%s]", setterMethodName,
+ safeToString(target), type));
}
if (logger.isDebugEnabled()) {
- logger.debug("Invoking setter method '" + setterMethodName + "' on target [" + target + "]");
+ logger.debug(String.format("Invoking setter method '%s' on %s with value [%s]", setterMethodName,
+ safeToString(target), value));
}
+
ReflectionUtils.makeAccessible(method);
ReflectionUtils.invokeMethod(method, target, value);
}
@@ -359,12 +374,12 @@ public class ReflectionTestUtils {
method = ReflectionUtils.findMethod(target.getClass(), getterMethodName);
}
if (method == null) {
- throw new IllegalArgumentException("Could not find getter method '" + getterMethodName +
- "' on target [" + target + "]");
+ throw new IllegalArgumentException(String.format(
+ "Could not find getter method '%s' on %s", getterMethodName, safeToString(target)));
}
if (logger.isDebugEnabled()) {
- logger.debug("Invoking getter method '" + getterMethodName + "' on target [" + target + "]");
+ logger.debug(String.format("Invoking getter method '%s' on %s", getterMethodName, safeToString(target)));
}
ReflectionUtils.makeAccessible(method);
return ReflectionUtils.invokeMethod(method, target);
@@ -399,8 +414,8 @@ public class ReflectionTestUtils {
methodInvoker.prepare();
if (logger.isDebugEnabled()) {
- logger.debug("Invoking method '" + name + "' on target [" + target + "] with arguments [" +
- ObjectUtils.nullSafeToString(args) + "]");
+ logger.debug(String.format("Invoking method '%s' on %s with arguments %s", name, safeToString(target),
+ ObjectUtils.nullSafeToString(args)));
}
return (T) methodInvoker.invoke();
@@ -411,4 +426,14 @@ public class ReflectionTestUtils {
}
}
+ private static String safeToString(Object target) {
+ try {
+ return String.format("target object [%s]", target);
+ }
+ catch (Exception ex) {
+ return String.format("target of type [%s] whose toString() method threw [%s]",
+ (target != null ? target.getClass().getName() : "unknown"), ex);
+ }
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java b/spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java
new file mode 100644
index 00000000..37139cb7
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.Assert;
+
+/**
+ * Base class for {@code RequestExpectationManager} implementations responsible
+ * for storing expectations and actual requests, and checking for unsatisfied
+ * expectations at the end.
+ *
+ * <p>Subclasses are responsible for validating each request by matching it to
+ * to expectations following the order of declaration or not.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.3
+ */
+public abstract class AbstractRequestExpectationManager implements RequestExpectationManager {
+
+ private final List<RequestExpectation> expectations = new LinkedList<RequestExpectation>();
+
+ private final List<ClientHttpRequest> requests = new LinkedList<ClientHttpRequest>();
+
+
+ protected List<RequestExpectation> getExpectations() {
+ return this.expectations;
+ }
+
+ protected List<ClientHttpRequest> getRequests() {
+ return this.requests;
+ }
+
+
+ @Override
+ public ResponseActions expectRequest(ExpectedCount count, RequestMatcher matcher) {
+ Assert.state(getRequests().isEmpty(), "Cannot add more expectations after actual requests are made");
+ RequestExpectation expectation = new DefaultRequestExpectation(count, matcher);
+ getExpectations().add(expectation);
+ return expectation;
+ }
+
+ @Override
+ public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException {
+ if (getRequests().isEmpty()) {
+ afterExpectationsDeclared();
+ }
+ ClientHttpResponse response = validateRequestInternal(request);
+ getRequests().add(request);
+ return response;
+ }
+
+ /**
+ * Invoked after the phase of declaring expected requests is over. This is
+ * detected from {@link #validateRequest} on the first actual request.
+ */
+ protected void afterExpectationsDeclared() {
+ }
+
+ /**
+ * Subclasses must implement the actual validation of the request
+ * matching it to a declared expectation.
+ */
+ protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException;
+
+ @Override
+ public void verify() {
+ if (getExpectations().isEmpty()) {
+ return;
+ }
+ int count = 0;
+ for (RequestExpectation expectation : getExpectations()) {
+ if (!expectation.isSatisfied()) {
+ count++;
+ }
+ }
+ if (count > 0) {
+ String message = "Further request(s) expected leaving " + count + " unsatisfied expectation(s).\n";
+ throw new AssertionError(message + getRequestDetails());
+ }
+ }
+
+ /**
+ * Return details of executed requests.
+ */
+ protected String getRequestDetails() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getRequests().size()).append(" request(s) executed");
+ if (!getRequests().isEmpty()) {
+ sb.append(":\n");
+ for (ClientHttpRequest request : getRequests()) {
+ sb.append(request.toString()).append("\n");
+ }
+ }
+ else {
+ sb.append(".\n");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Return an {@code AssertionError} that a sub-class can raise for an
+ * unexpected request.
+ */
+ protected AssertionError createUnexpectedRequestError(ClientHttpRequest request) {
+ HttpMethod method = request.getMethod();
+ URI uri = request.getURI();
+ String message = "No further requests expected: HTTP " + method + " " + uri + "\n";
+ return new AssertionError(message + getRequestDetails());
+ }
+
+ @Override
+ public void reset() {
+ this.expectations.clear();
+ this.requests.clear();
+ }
+
+
+ /**
+ * Helper class to manage a group of request expectations. It helps with
+ * operations against the entire group such as finding a match and updating
+ * (add or remove) based on expected request count.
+ */
+ protected static class RequestExpectationGroup {
+
+ private final Set<RequestExpectation> expectations = new LinkedHashSet<RequestExpectation>();
+
+ public Set<RequestExpectation> getExpectations() {
+ return this.expectations;
+ }
+
+ public void update(RequestExpectation expectation) {
+ if (expectation.hasRemainingCount()) {
+ getExpectations().add(expectation);
+ }
+ else {
+ getExpectations().remove(expectation);
+ }
+ }
+
+ public void updateAll(Collection<RequestExpectation> expectations) {
+ for (RequestExpectation expectation : expectations) {
+ update(expectation);
+ }
+ }
+
+ public RequestExpectation findExpectation(ClientHttpRequest request) throws IOException {
+ for (RequestExpectation expectation : getExpectations()) {
+ try {
+ expectation.match(request);
+ return expectation;
+ }
+ catch (AssertionError error) {
+ // Ignore
+ }
+ }
+ return null;
+ }
+
+ public void reset() {
+ this.expectations.clear();
+ }
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/DefaultRequestExpectation.java b/spring-test/src/main/java/org/springframework/test/web/client/DefaultRequestExpectation.java
new file mode 100644
index 00000000..93a4752a
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/client/DefaultRequestExpectation.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.Assert;
+
+/**
+ * Default implementation of {@code RequestExpectation} that simply delegates
+ * to the request matchers and the response creator it contains.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.3
+ */
+public class DefaultRequestExpectation implements RequestExpectation {
+
+ private final RequestCount requestCount;
+
+ private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
+
+ private ResponseCreator responseCreator;
+
+
+ /**
+ * Create a new request expectation that should be called a number of times
+ * as indicated by {@code RequestCount}.
+ * @param expectedCount the expected request expectedCount
+ */
+ public DefaultRequestExpectation(ExpectedCount expectedCount, RequestMatcher requestMatcher) {
+ Assert.notNull(expectedCount, "'expectedCount' is required");
+ Assert.notNull(requestMatcher, "'requestMatcher' is required");
+ this.requestCount = new RequestCount(expectedCount);
+ this.requestMatchers.add(requestMatcher);
+ }
+
+
+ protected RequestCount getRequestCount() {
+ return this.requestCount;
+ }
+
+ protected List<RequestMatcher> getRequestMatchers() {
+ return this.requestMatchers;
+ }
+
+ protected ResponseCreator getResponseCreator() {
+ return this.responseCreator;
+ }
+
+ @Override
+ public ResponseActions andExpect(RequestMatcher requestMatcher) {
+ Assert.notNull(requestMatcher, "RequestMatcher is required");
+ this.requestMatchers.add(requestMatcher);
+ return this;
+ }
+
+ @Override
+ public void andRespond(ResponseCreator responseCreator) {
+ Assert.notNull(responseCreator, "ResponseCreator is required");
+ this.responseCreator = responseCreator;
+ }
+
+ @Override
+ public void match(ClientHttpRequest request) throws IOException {
+ for (RequestMatcher matcher : getRequestMatchers()) {
+ matcher.match(request);
+ }
+ }
+
+ @Override
+ public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
+ ResponseCreator responseCreator = getResponseCreator();
+ if (responseCreator == null) {
+ throw new IllegalStateException("createResponse called before ResponseCreator was set");
+ }
+ getRequestCount().incrementAndValidate();
+ return responseCreator.createResponse(request);
+ }
+
+ @Override
+ public boolean hasRemainingCount() {
+ return getRequestCount().hasRemainingCount();
+ }
+
+ @Override
+ public boolean isSatisfied() {
+ return getRequestCount().isSatisfied();
+ }
+
+
+ /**
+ * Helper class that keeps track of actual vs expected request count.
+ */
+ protected static class RequestCount {
+
+ private final ExpectedCount expectedCount;
+
+ private int matchedRequestCount;
+
+ public RequestCount(ExpectedCount expectedCount) {
+ this.expectedCount = expectedCount;
+ }
+
+ public ExpectedCount getExpectedCount() {
+ return this.expectedCount;
+ }
+
+ public int getMatchedRequestCount() {
+ return this.matchedRequestCount;
+ }
+
+ public void incrementAndValidate() {
+ this.matchedRequestCount++;
+ if (getMatchedRequestCount() > getExpectedCount().getMaxCount()) {
+ throw new AssertionError("No more calls expected.");
+ }
+ }
+
+ public boolean hasRemainingCount() {
+ return (getMatchedRequestCount() < getExpectedCount().getMaxCount());
+ }
+
+ public boolean isSatisfied() {
+ return (getMatchedRequestCount() >= getExpectedCount().getMinCount());
+ }
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/ExpectedCount.java b/spring-test/src/main/java/org/springframework/test/web/client/ExpectedCount.java
new file mode 100644
index 00000000..09f3077c
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/client/ExpectedCount.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import org.springframework.util.Assert;
+
+/**
+ * A simple type representing a range for an expected count.
+ *
+ * <p>Examples:
+ * <pre>
+ * import static org.springframework.test.web.client.ExpectedCount.*
+ *
+ * once()
+ * manyTimes()
+ * times(5)
+ * min(2)
+ * max(4)
+ * between(2, 4)
+ * </pre>
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.3
+ */
+public class ExpectedCount {
+
+ private final int minCount;
+
+ private final int maxCount;
+
+
+ /**
+ * Private constructor.
+ * See static factory methods in this class.
+ */
+ private ExpectedCount(int minCount, int maxCount) {
+ Assert.isTrue(minCount >= 1, "minCount >= 0 is required");
+ Assert.isTrue(maxCount >= minCount, "maxCount >= minCount is required");
+ this.minCount = minCount;
+ this.maxCount = maxCount;
+ }
+
+
+ /**
+ * Return the {@code min} boundary of the expected count range.
+ */
+ public int getMinCount() {
+ return this.minCount;
+ }
+
+ /**
+ * Return the {@code max} boundary of the expected count range.
+ */
+ public int getMaxCount() {
+ return this.maxCount;
+ }
+
+
+ /**
+ * Exactly once.
+ */
+ public static ExpectedCount once() {
+ return new ExpectedCount(1, 1);
+ }
+
+ /**
+ * Many times (range of 1..Integer.MAX_VALUE).
+ */
+ public static ExpectedCount manyTimes() {
+ return new ExpectedCount(1, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Exactly N times.
+ */
+ public static ExpectedCount times(int count) {
+ Assert.isTrue(count >= 1, "'count' must be >= 1");
+ return new ExpectedCount(count, count);
+ }
+
+ /**
+ * At least {@code min} number of times.
+ */
+ public static ExpectedCount min(int min) {
+ Assert.isTrue(min >= 1, "'min' must be >= 1");
+ return new ExpectedCount(min, Integer.MAX_VALUE);
+ }
+
+ /**
+ * At most {@code max} number of times.
+ */
+ public static ExpectedCount max(int max) {
+ Assert.isTrue(max >= 1, "'max' must be >= 1");
+ return new ExpectedCount(1, max);
+ }
+
+ /**
+ * Between {@code min} and {@code max} number of times.
+ */
+ public static ExpectedCount between(int min, int max) {
+ return new ExpectedCount(min, max);
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java b/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java
index 7c7b6115..d1086a53 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -18,6 +18,7 @@ package org.springframework.test.web.client;
import java.io.IOException;
import java.net.URI;
+import java.nio.charset.Charset;
import java.util.List;
import org.springframework.http.HttpHeaders;
@@ -43,6 +44,8 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
*/
public class MockMvcClientHttpRequestFactory implements ClientHttpRequestFactory {
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
private final MockMvc mockMvc;
@@ -50,6 +53,7 @@ public class MockMvcClientHttpRequestFactory implements ClientHttpRequestFactory
this.mockMvc = mockMvc;
}
+
@Override
public ClientHttpRequest createRequest(final URI uri, final HttpMethod httpMethod) throws IOException {
return new MockClientHttpRequest(httpMethod, uri) {
@@ -73,7 +77,7 @@ public class MockMvcClientHttpRequestFactory implements ClientHttpRequestFactory
return clientResponse;
}
catch (Exception ex) {
- byte[] body = ex.toString().getBytes("UTF-8");
+ byte[] body = ex.toString().getBytes(UTF8_CHARSET);
return new MockClientHttpResponse(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java b/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java
index e1376041..09207f8c 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/MockRestServiceServer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -18,17 +18,14 @@ package org.springframework.test.web.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.AsyncClientHttpRequest;
import org.springframework.http.client.AsyncClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
-import org.springframework.test.web.client.match.MockRestRequestMatchers;
-import org.springframework.test.web.client.response.MockRestResponseCreators;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
import org.springframework.util.Assert;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;
@@ -36,53 +33,33 @@ 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.
+ * that involve direct or indirect use of the {@link RestTemplate}. Provides a
+ * way to set up expected requests that will be performed through the
+ * {@code RestTemplate} as well as mock responses to send back thus removing the
+ * need for an actual server.
*
- * <p>Below is an example:
- * <pre class="code">
- * import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
- * import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
- * import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
- *
- * ...
+ * <p>Below is an example that assumes static imports from
+ * {@code MockRestRequestMatchers}, {@code MockRestResponseCreators},
+ * and {@code ExpectedCount}:
*
+ * <pre class="code">
* RestTemplate restTemplate = new RestTemplate()
- * MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
+ * MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
*
- * mockServer.expect(requestTo("/hotels/42")).andExpect(method(HttpMethod.GET))
+ * server.expect(manyTimes(), 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();
+ * // Verify all expectations met
+ * server.verify();
* </pre>
*
- * <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 MockRestRequestMatchers}
- * and the {@code ResponseCreator} implementations provided in
- * {@link MockRestResponseCreators} 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}.
+ * <p>Note that as an alternative to the above you can also set the
+ * {@link MockMvcClientHttpRequestFactory} on a {@code RestTemplate} which
+ * allows executing requests against an instance of
+ * {@link org.springframework.test.web.servlet.MockMvc MockMvc}.
*
* @author Craig Walls
* @author Rossen Stoyanchev
@@ -90,143 +67,230 @@ import org.springframework.web.client.support.RestGatewaySupport;
*/
public class MockRestServiceServer {
- private final List<RequestMatcherClientHttpRequest> expectedRequests =
- new LinkedList<RequestMatcherClientHttpRequest>();
+ private final RequestExpectationManager expectationManager;
+
+
+ /**
+ * Private constructor with {@code RequestExpectationManager}.
+ * See static builder methods and {@code createServer} shortcut methods.
+ */
+ private MockRestServiceServer(RequestExpectationManager expectationManager) {
+ this.expectationManager = expectationManager;
+ }
+
+
+ /**
+ * Set up an expectation for a single HTTP request. The returned
+ * {@link ResponseActions} can be used to set up further expectations as
+ * well as to define the response.
+ * <p>This method may be invoked any number times before starting to make
+ * request through the underlying {@code RestTemplate} in order to set up
+ * all expected requests.
+ * @param matcher request matcher
+ * @return a representation of the expectation
+ */
+ public ResponseActions expect(RequestMatcher matcher) {
+ return expect(ExpectedCount.once(), matcher);
+ }
+
+ /**
+ * An alternative to {@link #expect(RequestMatcher)} with an indication how
+ * many times the request is expected to be executed.
+ * <p>When request expectations have an expected count greater than one, only
+ * the first execution is expected to match the order of declaration. Subsequent
+ * request executions may be inserted anywhere thereafter.
+ * @param count the expected count
+ * @param matcher request matcher
+ * @return a representation of the expectation
+ * @since 4.3
+ */
+ public ResponseActions expect(ExpectedCount count, RequestMatcher matcher) {
+ return this.expectationManager.expectRequest(count, matcher);
+ }
+
+ /**
+ * 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() {
+ this.expectationManager.verify();
+ }
- private final List<RequestMatcherClientHttpRequest> actualRequests =
- new LinkedList<RequestMatcherClientHttpRequest>();
+ /**
+ * Reset the internal state removing all expectations and recorded requests.
+ */
+ public void reset() {
+ this.expectationManager.reset();
+ }
+
+
+ /**
+ * Return a builder for a {@code MockRestServiceServer} that should be used
+ * to reply to the given {@code RestTemplate}.
+ * @since 4.3
+ */
+ public static MockRestServiceServerBuilder bindTo(RestTemplate restTemplate) {
+ return new DefaultBuilder(restTemplate);
+ }
+ /**
+ * Return a builder for a {@code MockRestServiceServer} that should be used
+ * to reply to the given {@code AsyncRestTemplate}.
+ * @since 4.3
+ */
+ public static MockRestServiceServerBuilder bindTo(AsyncRestTemplate asyncRestTemplate) {
+ return new DefaultBuilder(asyncRestTemplate);
+ }
/**
- * Private constructor.
- * @see #createServer(RestTemplate)
- * @see #createServer(RestGatewaySupport)
+ * Return a builder for a {@code MockRestServiceServer} that should be used
+ * to reply to the given {@code RestGatewaySupport}.
+ * @since 4.3
*/
- private MockRestServiceServer() {
+ public static MockRestServiceServerBuilder bindTo(RestGatewaySupport restGateway) {
+ Assert.notNull(restGateway, "'gatewaySupport' must not be null");
+ return new DefaultBuilder(restGateway.getRestTemplate());
}
/**
- * Create a {@code MockRestServiceServer} and set up the given
- * {@code RestTemplate} with a mock {@link ClientHttpRequestFactory}.
+ * A shortcut for {@code bindTo(restTemplate).build()}.
* @param restTemplate the RestTemplate to set up for mock testing
- * @return the created mock server
+ * @return the 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;
+ return bindTo(restTemplate).build();
}
/**
- * Create a {@code MockRestServiceServer} and set up the given
- * {@code AsyRestTemplate} with a mock {@link AsyncClientHttpRequestFactory}.
+ * A shortcut for {@code bindTo(asyncRestTemplate).build()}.
* @param asyncRestTemplate the AsyncRestTemplate to set up for mock testing
* @return the created mock server
*/
public static MockRestServiceServer createServer(AsyncRestTemplate asyncRestTemplate) {
- Assert.notNull(asyncRestTemplate, "'asyncRestTemplate' must not be null");
- MockRestServiceServer mockServer = new MockRestServiceServer();
- RequestMatcherClientHttpRequestFactory factory = mockServer.new RequestMatcherClientHttpRequestFactory();
- asyncRestTemplate.setAsyncRequestFactory(factory);
- return mockServer;
+ return bindTo(asyncRestTemplate).build();
}
/**
- * Create a {@code MockRestServiceServer} and set up the given
- * {@code RestGatewaySupport} with a mock {@link ClientHttpRequestFactory}.
+ * A shortcut for {@code bindTo(restGateway).build()}.
* @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());
+ return bindTo(restGateway).build();
}
/**
- * 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 MockRestRequestMatchers}
- * @return used to set up further expectations or to define a response
+ * Builder to create a {@code MockRestServiceServer}.
*/
- 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;
+ public interface MockRestServiceServerBuilder {
+
+ /**
+ * Whether to allow expected requests to be executed in any order not
+ * necessarily matching the order of declaration.
+ * <p>When set to "true" this is effectively a shortcut for:<br>
+ * {@code builder.build(new UnorderedRequestExpectationManager)}.
+ * @param ignoreExpectOrder whether to ignore the order of expectations
+ */
+ MockRestServiceServerBuilder ignoreExpectOrder(boolean ignoreExpectOrder);
+
+ /**
+ * Build the {@code MockRestServiceServer} and set up the underlying
+ * {@code RestTemplate} or {@code AsyncRestTemplate} with a
+ * {@link ClientHttpRequestFactory} that creates mock requests.
+ */
+ MockRestServiceServer build();
+
+ /**
+ * An overloaded build alternative that accepts a custom
+ * {@link RequestExpectationManager}.
+ */
+ MockRestServiceServer build(RequestExpectationManager manager);
}
- /**
- * 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;
+
+ private static class DefaultBuilder implements MockRestServiceServerBuilder {
+
+ private final RestTemplate restTemplate;
+
+ private final AsyncRestTemplate asyncRestTemplate;
+
+ private boolean ignoreExpectOrder;
+
+ public DefaultBuilder(RestTemplate restTemplate) {
+ Assert.notNull(restTemplate, "RestTemplate must not be null");
+ this.restTemplate = restTemplate;
+ this.asyncRestTemplate = null;
+ }
+
+ public DefaultBuilder(AsyncRestTemplate asyncRestTemplate) {
+ Assert.notNull(asyncRestTemplate, "AsyncRestTemplate must not be null");
+ this.restTemplate = null;
+ this.asyncRestTemplate = asyncRestTemplate;
+ }
+
+ @Override
+ public MockRestServiceServerBuilder ignoreExpectOrder(boolean ignoreExpectOrder) {
+ this.ignoreExpectOrder = ignoreExpectOrder;
+ return this;
}
- 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 ");
+ @Override
+ public MockRestServiceServer build() {
+ if (this.ignoreExpectOrder) {
+ return build(new UnorderedRequestExpectationManager());
+ }
+ else {
+ return build(new SimpleRequestExpectationManager());
+ }
}
- 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");
+ @Override
+ public MockRestServiceServer build(RequestExpectationManager manager) {
+ MockRestServiceServer server = new MockRestServiceServer(manager);
+ MockClientHttpRequestFactory factory = server.new MockClientHttpRequestFactory();
+ if (this.restTemplate != null) {
+ this.restTemplate.setRequestFactory(factory);
+ }
+ if (this.asyncRestTemplate != null) {
+ this.asyncRestTemplate.setAsyncRequestFactory(factory);
}
+ return server;
}
- return sb.toString();
}
/**
* Mock ClientHttpRequestFactory that creates requests by iterating
- * over the list of expected {@link RequestMatcherClientHttpRequest}'s.
+ * over the list of expected {@link DefaultRequestExpectation}'s.
*/
- private class RequestMatcherClientHttpRequestFactory
- implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
-
- private Iterator<RequestMatcherClientHttpRequest> requestIterator;
+ private class MockClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
@Override
- public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
+ public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
return createRequestInternal(uri, httpMethod);
}
@Override
- public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
+ public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) {
return createRequestInternal(uri, httpMethod);
}
- private RequestMatcherClientHttpRequest createRequestInternal(URI uri, HttpMethod httpMethod) {
+ private MockAsyncClientHttpRequest createRequestInternal(URI uri, HttpMethod method) {
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: HTTP " + httpMethod + " " + uri);
- }
+ Assert.notNull(method, "'httpMethod' must not be null");
- RequestMatcherClientHttpRequest request = this.requestIterator.next();
- request.setURI(uri);
- request.setMethod(httpMethod);
+ return new MockAsyncClientHttpRequest(method, uri) {
- MockRestServiceServer.this.actualRequests.add(request);
- return request;
+ @Override
+ protected ClientHttpResponse executeInternal() throws IOException {
+ ClientHttpResponse response = expectationManager.validateRequest(this);
+ setResponse(response);
+ return response;
+ }
+ };
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java b/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java
new file mode 100644
index 00000000..b2d8f144
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectation.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+/**
+ * An extension of {@code ResponseActions} that also implements
+ * {@code RequestMatcher} and {@code ResponseCreator}
+ *
+ * <p>While {@code ResponseActions} is the API for defining expectations this
+ * sub-interface is the internal SPI for matching these expectations to actual
+ * requests and for creating responses.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.3
+ */
+public interface RequestExpectation extends ResponseActions, RequestMatcher, ResponseCreator {
+
+ /**
+ * Whether there is a remaining count of invocations for this expectation.
+ */
+ boolean hasRemainingCount();
+
+ /**
+ * Whether the requirements for this request expectation have been met.
+ */
+ boolean isSatisfied();
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectationManager.java b/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectationManager.java
new file mode 100644
index 00000000..a79f9210
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/client/RequestExpectationManager.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.io.IOException;
+
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+
+/**
+ * Abstraction for creating HTTP request expectations, applying them to actual
+ * requests (in strict or random order), and verifying whether expectations
+ * have been met.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.3
+ */
+public interface RequestExpectationManager {
+
+ /**
+ * Set up a new request expectation. The returned {@link ResponseActions} is
+ * used to add more expectations and define a response.
+ * @param requestMatcher a request expectation
+ * @return for setting up further expectations and define a response
+ */
+ ResponseActions expectRequest(ExpectedCount count, RequestMatcher requestMatcher);
+
+ /**
+ * Validate the given actual request against the declared expectations.
+ * Is successful return the mock response to use or raise an error.
+ * @param request the request
+ * @return the response to return if the request was validated.
+ * @throws AssertionError when some expectations were not met
+ * @throws IOException
+ */
+ ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException;
+
+ /**
+ * Verify that all expectations have been met.
+ * @throws AssertionError when some expectations were not met
+ */
+ void verify();
+
+ /**
+ * Reset the internal state removing all expectations and recorded requests.
+ */
+ void reset();
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/RequestMatcher.java b/spring-test/src/main/java/org/springframework/test/web/client/RequestMatcher.java
index bc169f48..14237729 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/RequestMatcher.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/RequestMatcher.java
@@ -23,6 +23,9 @@ import org.springframework.http.client.ClientHttpRequest;
/**
* A contract for matching requests to expectations.
*
+ * <p>See {@link org.springframework.test.web.client.match.MockRestRequestMatchers
+ * MockRestRequestMatchers} for static factory methods.
+ *
* @author Craig Walls
* @since 3.2
*/
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java b/spring-test/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java
deleted file mode 100644
index 1a01328b..00000000
--- a/spring-test/src/main/java/org/springframework/test/web/client/RequestMatcherClientHttpRequest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2002-2015 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.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.MockAsyncClientHttpRequest;
-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 MockAsyncClientHttpRequest 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);
- }
-
-
- @Override
- public ResponseActions andExpect(RequestMatcher requestMatcher) {
- Assert.notNull(requestMatcher, "RequestMatcher is required");
- this.requestMatchers.add(requestMatcher);
- return this;
- }
-
- @Override
- public void andRespond(ResponseCreator responseCreator) {
- Assert.notNull(responseCreator, "ResponseCreator is required");
- this.responseCreator = responseCreator;
- }
-
- @Override
- public ClientHttpResponse executeInternal() 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.executeInternal();
- }
-
-}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java b/spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java
new file mode 100644
index 00000000..dbcf3852
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/client/SimpleRequestExpectationManager.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.Assert;
+
+/**
+ * Simple {@code RequestExpectationManager} that matches requests to expectations
+ * sequentially, i.e. in the order of declaration of expectations.
+ *
+ * <p>When request expectations have an expected count greater than one,
+ * only the first execution is expected to match the order of declaration.
+ * Subsequent request executions may be inserted anywhere thereafter.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.3
+ */
+public class SimpleRequestExpectationManager extends AbstractRequestExpectationManager {
+
+ private Iterator<RequestExpectation> expectationIterator;
+
+ private final RequestExpectationGroup repeatExpectations = new RequestExpectationGroup();
+
+
+ @Override
+ protected void afterExpectationsDeclared() {
+ Assert.state(this.expectationIterator == null);
+ this.expectationIterator = getExpectations().iterator();
+ }
+
+ @Override
+ public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException {
+ RequestExpectation expectation;
+ try {
+ expectation = next(request);
+ expectation.match(request);
+ }
+ catch (AssertionError error) {
+ expectation = this.repeatExpectations.findExpectation(request);
+ if (expectation == null) {
+ throw error;
+ }
+ }
+ ClientHttpResponse response = expectation.createResponse(request);
+ this.repeatExpectations.update(expectation);
+ return response;
+ }
+
+ private RequestExpectation next(ClientHttpRequest request) {
+ if (this.expectationIterator.hasNext()) {
+ return this.expectationIterator.next();
+ }
+ throw createUnexpectedRequestError(request);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ this.expectationIterator = null;
+ this.repeatExpectations.reset();
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java b/spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java
new file mode 100644
index 00000000..42c1a6aa
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/client/UnorderedRequestExpectationManager.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.io.IOException;
+
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+
+/**
+ * {@code RequestExpectationManager} that matches requests to expectations
+ * regardless of the order of declaration of expected requests.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.3
+ */
+public class UnorderedRequestExpectationManager extends AbstractRequestExpectationManager {
+
+ private final RequestExpectationGroup remainingExpectations = new RequestExpectationGroup();
+
+
+ @Override
+ protected void afterExpectationsDeclared() {
+ this.remainingExpectations.updateAll(getExpectations());
+ }
+
+ @Override
+ public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException {
+ RequestExpectation expectation = this.remainingExpectations.findExpectation(request);
+ if (expectation != null) {
+ ClientHttpResponse response = expectation.createResponse(request);
+ this.remainingExpectations.update(expectation);
+ return response;
+ }
+ throw createUnexpectedRequestError(request);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ this.remainingExpectations.reset();
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
index 4acbf82e..e32a68aa 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,18 +16,24 @@
package org.springframework.test.web.client.match;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import org.hamcrest.Matcher;
import org.w3c.dom.Node;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.test.web.client.RequestMatcher;
+import org.springframework.util.MultiValueMap;
import static org.hamcrest.MatcherAssert.*;
import static org.springframework.test.util.AssertionErrors.*;
@@ -137,13 +143,36 @@ public class ContentRequestMatchers {
}
/**
+ * Parse the body as form data and compare to the given {@code MultiValueMap}.
+ * @since 4.3
+ */
+ public RequestMatcher formData(final MultiValueMap<String, String> expectedContent) {
+ return new RequestMatcher() {
+ @Override
+ public void match(final ClientHttpRequest request) throws IOException, AssertionError {
+ HttpInputMessage inputMessage = new HttpInputMessage() {
+ @Override
+ public InputStream getBody() throws IOException {
+ MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
+ return new ByteArrayInputStream(mockRequest.getBodyAsBytes());
+ }
+ @Override
+ public HttpHeaders getHeaders() {
+ return request.getHeaders();
+ }
+ };
+ FormHttpMessageConverter converter = new FormHttpMessageConverter();
+ assertEquals("Request content", expectedContent, converter.read(null, inputMessage));
+ }
+ };
+ }
+
+ /**
* 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) {
@@ -180,6 +209,7 @@ public class ContentRequestMatchers {
};
}
+
/**
* Abstract base class for XML {@link RequestMatcher}'s.
*/
@@ -191,12 +221,13 @@ public class ContentRequestMatchers {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
matchInternal(mockRequest);
}
- catch (Exception e) {
- throw new AssertionError("Failed to parse expected or actual XML request content: " + e.getMessage());
+ catch (Exception ex) {
+ throw new AssertionError("Failed to parse expected or actual XML request content: " + ex.getMessage());
}
}
protected abstract void matchInternal(MockClientHttpRequest request) throws Exception;
-
}
+
}
+
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java
index 318e9d88..d9d78bb9 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java
@@ -123,7 +123,7 @@ public abstract class MockRestRequestMatchers {
/**
* Assert request header values with the given Hamcrest matcher.
*/
- @SuppressWarnings("unchecked")
+ @SafeVarargs
public static RequestMatcher header(final String name, final Matcher<? super String>... matchers) {
return new RequestMatcher() {
@Override
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java b/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java
index 3f2be1ae..213bc5d7 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.test.web.client.response;
import java.io.IOException;
import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
import java.net.URI;
+import java.nio.charset.Charset;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
@@ -38,6 +39,9 @@ import org.springframework.util.Assert;
*/
public class DefaultResponseCreator implements ResponseCreator {
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+
private byte[] content;
private Resource contentResource;
@@ -56,6 +60,7 @@ public class DefaultResponseCreator implements ResponseCreator {
this.statusCode = statusCode;
}
+
@Override
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
MockClientHttpResponse response;
@@ -74,13 +79,7 @@ public class DefaultResponseCreator implements ResponseCreator {
* 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);
- }
+ this.content = content.getBytes(UTF8_CHARSET);
return this;
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java
index 3db5711d..1f64c74e 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java
@@ -37,8 +37,7 @@ import org.springframework.util.Assert;
* WebClient webClient = new WebClient();
*
* MockMvc mockMvc = ...
- * MockMvcWebConnection mockConnection = new MockMvcWebConnection(mockMvc);
- * mockConnection.setWebClient(webClient);
+ * MockMvcWebConnection mockConnection = new MockMvcWebConnection(mockMvc, webClient);
*
* WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
* WebConnection httpConnection = new HttpWebConnection(webClient);
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java
index 497bf4ab..597ac8f4 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java
@@ -106,7 +106,7 @@ public class MockMvcWebClientBuilder extends MockMvcWebConnectionBuilderSupport<
*/
public MockMvcWebClientBuilder withDelegate(WebClient webClient) {
Assert.notNull(webClient, "WebClient must not be null");
- webClient.setWebConnection(createConnection(webClient.getWebConnection()));
+ webClient.setWebConnection(createConnection(webClient));
this.webClient = webClient;
return this;
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java
index 2f19cea7..fd4ef702 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -17,13 +17,16 @@
package org.springframework.test.web.servlet.htmlunit;
import java.io.IOException;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import com.gargoylesoftware.htmlunit.CookieManager;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebConnection;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
+import com.gargoylesoftware.htmlunit.util.Cookie;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
@@ -41,8 +44,7 @@ import org.springframework.util.Assert;
* <pre class="code">
* WebClient webClient = new WebClient();
* MockMvc mockMvc = ...
- * MockMvcWebConnection webConnection = new MockMvcWebConnection(mockMvc);
- * mockConnection.setWebClient(webClient);
+ * MockMvcWebConnection webConnection = new MockMvcWebConnection(mockMvc, webClient);
* webClient.setWebConnection(webConnection);
*
* // Use webClient as normal ...
@@ -70,9 +72,10 @@ public final class MockMvcWebConnection implements WebConnection {
* <p>For example, the URL {@code http://localhost/test/this} would use
* {@code ""} as the context path.
* @param mockMvc the {@code MockMvc} instance to use; never {@code null}
+ * @param webClient the {@link WebClient} to use. never {@code null}
*/
- public MockMvcWebConnection(MockMvc mockMvc) {
- this(mockMvc, "");
+ public MockMvcWebConnection(MockMvc mockMvc, WebClient webClient) {
+ this(mockMvc, webClient, "");
}
/**
@@ -83,17 +86,68 @@ public final class MockMvcWebConnection implements WebConnection {
* which states that it can be an empty string and otherwise must start
* with a "/" character and not end with a "/" character.
* @param mockMvc the {@code MockMvc} instance to use; never {@code null}
+ * @param webClient the {@link WebClient} to use. never {@code null}
* @param contextPath the contextPath to use
*/
- public MockMvcWebConnection(MockMvc mockMvc, String contextPath) {
+ public MockMvcWebConnection(MockMvc mockMvc, WebClient webClient, String contextPath) {
Assert.notNull(mockMvc, "MockMvc must not be null");
+ Assert.notNull(webClient, "WebClient must not be null");
validateContextPath(contextPath);
- this.webClient = new WebClient();
+ this.webClient = webClient;
this.mockMvc = mockMvc;
this.contextPath = contextPath;
}
+ /**
+ * Create a new instance that assumes the context path of the application
+ * is {@code ""} (i.e., the root context).
+ * <p>For example, the URL {@code http://localhost/test/this} would use
+ * {@code ""} as the context path.
+ * @param mockMvc the {@code MockMvc} instance to use; never {@code null}
+ * @deprecated Use {@link #MockMvcWebConnection(MockMvc, WebClient)}
+ */
+ @Deprecated
+ public MockMvcWebConnection(MockMvc mockMvc) {
+ this(mockMvc, "");
+ }
+
+ /**
+ * Create a new instance with the specified context path.
+ * <p>The path may be {@code null} in which case the first path segment
+ * of the URL is turned into the contextPath. Otherwise it must conform
+ * to {@link javax.servlet.http.HttpServletRequest#getContextPath()}
+ * which states that it can be an empty string and otherwise must start
+ * with a "/" character and not end with a "/" character.
+ * @param mockMvc the {@code MockMvc} instance to use; never {@code null}
+ * @param contextPath the contextPath to use
+ * @deprecated use {@link #MockMvcWebConnection(MockMvc, WebClient, String)}
+ */
+ @Deprecated
+ public MockMvcWebConnection(MockMvc mockMvc, String contextPath) {
+ this(mockMvc, new WebClient(), contextPath);
+ }
+
+ /**
+ * Validate the supplied {@code contextPath}.
+ * <p>If the value is not {@code null}, it must conform to
+ * {@link javax.servlet.http.HttpServletRequest#getContextPath()} which
+ * states that it can be an empty string and otherwise must start with
+ * a "/" character and not end with a "/" character.
+ * @param contextPath the path to validate
+ */
+ static void validateContextPath(String contextPath) {
+ if (contextPath == null || "".equals(contextPath)) {
+ return;
+ }
+ if (!contextPath.startsWith("/")) {
+ throw new IllegalArgumentException("contextPath '" + contextPath + "' must start with '/'.");
+ }
+ if (contextPath.endsWith("/")) {
+ throw new IllegalArgumentException("contextPath '" + contextPath + "' must not end with '/'.");
+ }
+ }
+
public void setWebClient(WebClient webClient) {
Assert.notNull(webClient, "WebClient must not be null");
@@ -113,6 +167,7 @@ public final class MockMvcWebConnection implements WebConnection {
httpServletResponse = getResponse(requestBuilder);
forwardedUrl = httpServletResponse.getForwardedUrl();
}
+ storeCookies(webRequest, httpServletResponse.getCookies());
return new MockWebResponseBuilder(startTime, webRequest, httpServletResponse).build();
}
@@ -129,29 +184,29 @@ public final class MockMvcWebConnection implements WebConnection {
return resultActions.andReturn().getResponse();
}
- @Override
- public void close() {
- }
-
-
- /**
- * Validate the supplied {@code contextPath}.
- * <p>If the value is not {@code null}, it must conform to
- * {@link javax.servlet.http.HttpServletRequest#getContextPath()} which
- * states that it can be an empty string and otherwise must start with
- * a "/" character and not end with a "/" character.
- * @param contextPath the path to validate
- */
- static void validateContextPath(String contextPath) {
- if (contextPath == null || "".equals(contextPath)) {
+ private void storeCookies(WebRequest webRequest, javax.servlet.http.Cookie[] cookies) {
+ if (cookies == null) {
return;
}
- if (!contextPath.startsWith("/")) {
- throw new IllegalArgumentException("contextPath '" + contextPath + "' must start with '/'.");
- }
- if (contextPath.endsWith("/")) {
- throw new IllegalArgumentException("contextPath '" + contextPath + "' must not end with '/'.");
+ Date now = new Date();
+ CookieManager cookieManager = this.webClient.getCookieManager();
+ for (javax.servlet.http.Cookie cookie : cookies) {
+ if (cookie.getDomain() == null) {
+ cookie.setDomain(webRequest.getUrl().getHost());
+ }
+ Cookie toManage = MockWebResponseBuilder.createCookie(cookie);
+ Date expires = toManage.getExpires();
+ if (expires == null || expires.after(now)) {
+ cookieManager.addCookie(toManage);
+ }
+ else {
+ cookieManager.removeCookie(toManage);
+ }
}
}
+ @Override
+ public void close() {
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java
index cc957dca..c173fdfb 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -19,9 +19,11 @@ package org.springframework.test.web.servlet.htmlunit;
import java.util.ArrayList;
import java.util.List;
+import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebConnection;
import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.htmlunit.DelegatingWebConnection.DelegateWebConnection;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.util.Assert;
@@ -43,7 +45,7 @@ public abstract class MockMvcWebConnectionBuilderSupport<T extends MockMvcWebCon
private final MockMvc mockMvc;
- private final List<WebRequestMatcher> mockMvcRequestMatchers = new ArrayList<WebRequestMatcher>();
+ private final List<WebRequestMatcher> requestMatchers = new ArrayList<WebRequestMatcher>();
private String contextPath = "";
@@ -57,7 +59,7 @@ public abstract class MockMvcWebConnectionBuilderSupport<T extends MockMvcWebCon
protected MockMvcWebConnectionBuilderSupport(MockMvc mockMvc) {
Assert.notNull(mockMvc, "MockMvc must not be null");
this.mockMvc = mockMvc;
- this.mockMvcRequestMatchers.add(new HostRequestMatcher("localhost"));
+ this.requestMatchers.add(new HostRequestMatcher("localhost"));
}
/**
@@ -116,7 +118,7 @@ public abstract class MockMvcWebConnectionBuilderSupport<T extends MockMvcWebCon
@SuppressWarnings("unchecked")
public T useMockMvc(WebRequestMatcher... matchers) {
for (WebRequestMatcher matcher : matchers) {
- this.mockMvcRequestMatchers.add(matcher);
+ this.requestMatchers.add(matcher);
}
return (T) this;
}
@@ -130,7 +132,7 @@ public abstract class MockMvcWebConnectionBuilderSupport<T extends MockMvcWebCon
*/
@SuppressWarnings("unchecked")
public T useMockMvcForHosts(String... hosts) {
- this.mockMvcRequestMatchers.add(new HostRequestMatcher(hosts));
+ this.requestMatchers.add(new HostRequestMatcher(hosts));
return (T) this;
}
@@ -145,21 +147,41 @@ public abstract class MockMvcWebConnectionBuilderSupport<T extends MockMvcWebCon
* @see #alwaysUseMockMvc()
* @see #useMockMvc(WebRequestMatcher...)
* @see #useMockMvcForHosts(String...)
+ * @deprecated Use {@link #createConnection(WebClient)} instead
*/
+ @Deprecated
protected final WebConnection createConnection(WebConnection defaultConnection) {
Assert.notNull(defaultConnection, "Default WebConnection must not be null");
- MockMvcWebConnection mockMvcWebConnection = new MockMvcWebConnection(this.mockMvc, this.contextPath);
+ return createConnection(new WebClient(), defaultConnection);
+ }
+ /**
+ * Create a new {@link WebConnection} that will use a {@link MockMvc}
+ * instance if one of the specified {@link WebRequestMatcher} instances
+ * matches.
+ * @param webClient the WebClient to use if none of the specified
+ * {@code WebRequestMatcher} instances matches (never {@code null})
+ * @return a new {@code WebConnection} that will use a {@code MockMvc}
+ * instance if one of the specified {@code WebRequestMatcher} matches
+ * @see #alwaysUseMockMvc()
+ * @see #useMockMvc(WebRequestMatcher...)
+ * @see #useMockMvcForHosts(String...)
+ * @since 4.3
+ */
+ protected final WebConnection createConnection(WebClient webClient) {
+ Assert.notNull(webClient, "WebClient must not be null");
+ return createConnection(webClient, webClient.getWebConnection());
+ }
+
+ private WebConnection createConnection(WebClient webClient, WebConnection defaultConnection) {
+ WebConnection connection = new MockMvcWebConnection(this.mockMvc, webClient, this.contextPath);
if (this.alwaysUseMockMvc) {
- return mockMvcWebConnection;
+ return connection;
}
-
- List<DelegatingWebConnection.DelegateWebConnection> delegates = new ArrayList<DelegatingWebConnection.DelegateWebConnection>(
- this.mockMvcRequestMatchers.size());
- for (WebRequestMatcher matcher : this.mockMvcRequestMatchers) {
- delegates.add(new DelegatingWebConnection.DelegateWebConnection(matcher, mockMvcWebConnection));
+ List<DelegateWebConnection> delegates = new ArrayList<DelegateWebConnection>(this.requestMatchers.size());
+ for (WebRequestMatcher matcher : this.requestMatchers) {
+ delegates.add(new DelegateWebConnection(matcher, connection));
}
-
return new DelegatingWebConnection(defaultConnection, delegates);
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java
index 7ead8e13..fd716f47 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -19,13 +19,17 @@ package org.springframework.test.web.servlet.htmlunit;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
+import javax.servlet.http.Cookie;
+
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.WebResponseData;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
+import org.apache.http.impl.cookie.BasicClientCookie;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.util.Assert;
@@ -34,6 +38,7 @@ import org.springframework.util.StringUtils;
/**
* @author Rob Winch
* @author Sam Brannen
+ * @author Rossen Stoyanchev
* @since 4.2
*/
final class MockWebResponseBuilder {
@@ -100,7 +105,30 @@ final class MockWebResponseBuilder {
if (location != null) {
responseHeaders.add(new NameValuePair("Location", location));
}
+ for (Cookie cookie : this.response.getCookies()) {
+ responseHeaders.add(new NameValuePair("Set-Cookie", valueOfCookie(cookie)));
+ }
return responseHeaders;
}
+ private String valueOfCookie(Cookie cookie) {
+ return createCookie(cookie).toString();
+ }
+
+ static com.gargoylesoftware.htmlunit.util.Cookie createCookie(Cookie cookie) {
+ Date expires = null;
+ if (cookie.getMaxAge() > -1) {
+ expires = new Date(System.currentTimeMillis() + cookie.getMaxAge() * 1000);
+ }
+ BasicClientCookie result = new BasicClientCookie(cookie.getName(), cookie.getValue());
+ result.setDomain(cookie.getDomain());
+ result.setComment(cookie.getComment());
+ result.setExpiryDate(expires);
+ result.setPath(cookie.getPath());
+ result.setSecure(cookie.getSecure());
+ if(cookie.isHttpOnly()) {
+ result.setAttribute("httponly", "true");
+ }
+ return new com.gargoylesoftware.htmlunit.util.Cookie(result);
+ }
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java
index 9454c0a3..9a5ab8ac 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java
@@ -129,7 +129,7 @@ public class MockMvcHtmlUnitDriverBuilder extends MockMvcWebConnectionBuilderSup
public MockMvcHtmlUnitDriverBuilder withDelegate(WebConnectionHtmlUnitDriver driver) {
Assert.notNull(driver, "HtmlUnitDriver must not be null");
driver.setJavascriptEnabled(this.javascriptEnabled);
- driver.setWebConnection(createConnection(driver.getWebConnection()));
+ driver.setWebConnection(createConnection(driver.getWebClient()));
this.driver = driver;
return this;
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java
index bdb67b87..e8fc5f18 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java
@@ -88,6 +88,14 @@ public class WebConnectionHtmlUnitDriver extends HtmlUnitDriver {
}
/**
+ * Return the current {@link WebClient}.
+ * @since 4.3
+ */
+ public WebClient getWebClient() {
+ return this.webClient;
+ }
+
+ /**
* Set the {@link WebConnection} to be used with the {@link WebClient}.
* @param webConnection the {@code WebConnection} to use (never {@code null})
*/
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java
index 1663bd2f..d181a46f 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,8 +16,12 @@
package org.springframework.test.web.servlet.request;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
+import java.nio.charset.Charset;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -33,8 +37,10 @@ import javax.servlet.http.Cookie;
import org.springframework.beans.Mergeable;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
+import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
@@ -60,16 +66,23 @@ import org.springframework.web.util.UriUtils;
*
* <p>Application tests will typically access this builder through the static factory
* methods in {@link MockMvcRequestBuilders}.
+ * <p>Although this class cannot be extended, additional ways to initialize
+ * the {@code MockHttpServletRequest} can be plugged in via
+ * {@link #with(RequestPostProcessor)}.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @author Sam Brannen
+ * @author Kamill Sokol
* @since 3.2
*/
public class MockHttpServletRequestBuilder
implements ConfigurableSmartRequestBuilder<MockHttpServletRequestBuilder>, Mergeable {
- private final HttpMethod method;
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+
+ private final String method;
private final URI url;
@@ -119,27 +132,33 @@ public class MockHttpServletRequestBuilder
* @param vars zero or more URL variables
*/
MockHttpServletRequestBuilder(HttpMethod httpMethod, String url, Object... vars) {
- this(httpMethod, UriComponentsBuilder.fromUriString(url).buildAndExpand(vars).encode().toUri());
+ this(httpMethod.name(), UriComponentsBuilder.fromUriString(url).buildAndExpand(vars).encode().toUri());
}
/**
- * Package private constructor. To get an instance, use static factory
- * methods in {@link MockMvcRequestBuilders}.
- * <p>Although this class cannot be extended, additional ways to initialize
- * the {@code MockHttpServletRequest} can be plugged in via
- * {@link #with(RequestPostProcessor)}.
+ * Alternative to {@link #MockHttpServletRequestBuilder(HttpMethod, String, Object...)}
+ * with a pre-built URI.
* @param httpMethod the HTTP method (GET, POST, etc)
* @param url the URL
* @since 4.0.3
*/
MockHttpServletRequestBuilder(HttpMethod httpMethod, URI url) {
- Assert.notNull(httpMethod, "httpMethod is required");
- Assert.notNull(url, "url is required");
+ this(httpMethod.name(), url);
+ }
+
+ /**
+ * Alternative constructor for custom HTTP methods.
+ * @param httpMethod the HTTP method (GET, POST, etc)
+ * @param url the URL
+ * @since 4.3
+ */
+ MockHttpServletRequestBuilder(String httpMethod, URI url) {
+ Assert.notNull(httpMethod, "'httpMethod' is required");
+ Assert.notNull(url, "'url' is required");
this.method = httpMethod;
this.url = url;
}
-
/**
* Add a request parameter to the {@link MockHttpServletRequest}.
* <p>If called more than once, new values get added to existing ones.
@@ -257,12 +276,7 @@ public class MockHttpServletRequestBuilder
* @param content the body content
*/
public MockHttpServletRequestBuilder content(String content) {
- try {
- this.content = content.getBytes("UTF-8");
- }
- catch (UnsupportedEncodingException e) {
- // should never happen
- }
+ this.content = content.getBytes(UTF8_CHARSET);
return this;
}
@@ -320,7 +334,7 @@ public class MockHttpServletRequestBuilder
* @param sessionAttributes the session attributes
*/
public MockHttpServletRequestBuilder sessionAttrs(Map<String, Object> sessionAttributes) {
- Assert.notEmpty(sessionAttributes, "'sessionAttrs' must not be empty");
+ Assert.notEmpty(sessionAttributes, "'sessionAttributes' must not be empty");
for (String name : sessionAttributes.keySet()) {
sessionAttr(name, sessionAttributes.get(name));
}
@@ -342,7 +356,7 @@ public class MockHttpServletRequestBuilder
* @param flashAttributes the flash attributes
*/
public MockHttpServletRequestBuilder flashAttrs(Map<String, Object> flashAttributes) {
- Assert.notEmpty(flashAttributes, "'flashAttrs' must not be empty");
+ Assert.notEmpty(flashAttributes, "'flashAttributes' must not be empty");
for (String name : flashAttributes.keySet()) {
flashAttr(name, flashAttributes.get(name));
}
@@ -585,7 +599,7 @@ public class MockHttpServletRequestBuilder
request.setServerPort(this.url.getPort());
}
- request.setMethod(this.method.name());
+ request.setMethod(this.method);
for (String name : this.headers.keySet()) {
for (Object value : this.headers.get(name)) {
@@ -593,24 +607,10 @@ public class MockHttpServletRequestBuilder
}
}
- try {
- if (this.url.getRawQuery() != null) {
- request.setQueryString(this.url.getRawQuery());
- }
-
- MultiValueMap<String, String> queryParams =
- UriComponentsBuilder.fromUri(this.url).build().getQueryParams();
-
- for (Entry<String, List<String>> entry : queryParams.entrySet()) {
- for (String value : entry.getValue()) {
- value = (value != null) ? UriUtils.decode(value, "UTF-8") : null;
- request.addParameter(UriUtils.decode(entry.getKey(), "UTF-8"), value);
- }
- }
- }
- catch (UnsupportedEncodingException ex) {
- // shouldn't happen
+ if (this.url.getRawQuery() != null) {
+ request.setQueryString(this.url.getRawQuery());
}
+ addRequestParams(request, UriComponentsBuilder.fromUri(this.url).build().getQueryParams());
for (String name : this.parameters.keySet()) {
for (String value : this.parameters.get(name)) {
@@ -622,6 +622,13 @@ public class MockHttpServletRequestBuilder
request.setContent(this.content);
request.setCharacterEncoding(this.characterEncoding);
+ if (this.content != null && this.contentType != null) {
+ MediaType mediaType = MediaType.parseMediaType(this.contentType);
+ if (MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)) {
+ addRequestParams(request, parseFormData(mediaType));
+ }
+ }
+
if (!ObjectUtils.isEmpty(this.cookies)) {
request.setCookies(this.cookies.toArray(new Cookie[this.cookies.size()]));
}
@@ -685,6 +692,44 @@ public class MockHttpServletRequestBuilder
request.setPathInfo(this.pathInfo);
}
+ private void addRequestParams(MockHttpServletRequest request, MultiValueMap<String, String> map) {
+ try {
+ for (Entry<String, List<String>> entry : map.entrySet()) {
+ for (String value : entry.getValue()) {
+ value = (value != null) ? UriUtils.decode(value, "UTF-8") : null;
+ request.addParameter(UriUtils.decode(entry.getKey(), "UTF-8"), value);
+ }
+ }
+ }
+ catch (UnsupportedEncodingException ex) {
+ // shouldn't happen
+ }
+ }
+
+ private MultiValueMap<String, String> parseFormData(final MediaType mediaType) {
+ MultiValueMap<String, String> map;
+ HttpInputMessage message = new HttpInputMessage() {
+ @Override
+ public InputStream getBody() throws IOException {
+ return new ByteArrayInputStream(content);
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(mediaType);
+ return headers;
+ }
+ };
+ try {
+ map = new FormHttpMessageConverter().read(null, message);
+ }
+ catch (IOException ex) {
+ throw new IllegalStateException("Failed to parse form data in request body", ex);
+ }
+ return map;
+ }
+
private FlashMapManager getFlashMapManager(MockHttpServletRequest request) {
FlashMapManager flashMapManager = null;
try {
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java
index 571699e6..f0e21adb 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -42,6 +42,7 @@ import org.springframework.test.web.servlet.RequestBuilder;
* @author Greg Turnquist
* @author Sebastien Deleuze
* @author Sam Brannen
+ * @author Kamill Sokol
* @since 3.2
*/
public abstract class MockMvcRequestBuilders {
@@ -49,10 +50,10 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for a GET request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, urlVars);
}
/**
@@ -67,10 +68,10 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for a POST request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(HttpMethod.POST, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(HttpMethod.POST, urlTemplate, urlVars);
}
/**
@@ -85,10 +86,10 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for a PUT request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(HttpMethod.PUT, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(HttpMethod.PUT, urlTemplate, urlVars);
}
/**
@@ -103,10 +104,10 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for a PATCH request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(HttpMethod.PATCH, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(HttpMethod.PATCH, urlTemplate, urlVars);
}
/**
@@ -121,10 +122,10 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for a DELETE request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, urlVars);
}
/**
@@ -139,10 +140,10 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, urlVars);
}
/**
@@ -157,11 +158,11 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for a HEAD request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
* @since 4.1
*/
- public static MockHttpServletRequestBuilder head(String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(HttpMethod.HEAD, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder head(String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(HttpMethod.HEAD, urlTemplate, urlVars);
}
/**
@@ -175,12 +176,12 @@ public abstract class MockMvcRequestBuilders {
/**
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP method.
- * @param httpMethod the HTTP method
+ * @param method the HTTP method (GET, POST, etc)
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) {
- return new MockHttpServletRequestBuilder(httpMethod, urlTemplate, urlVariables);
+ public static MockHttpServletRequestBuilder request(HttpMethod method, String urlTemplate, Object... urlVars) {
+ return new MockHttpServletRequestBuilder(method, urlTemplate, urlVars);
}
/**
@@ -194,12 +195,22 @@ public abstract class MockMvcRequestBuilders {
}
/**
+ * Alternative factory method that allows for custom HTTP verbs (e.g. WebDAV).
+ * @param httpMethod the HTTP method
+ * @param uri the URL
+ * @since 4.3
+ */
+ public static MockHttpServletRequestBuilder request(String httpMethod, URI uri) {
+ return new MockHttpServletRequestBuilder(httpMethod, uri);
+ }
+
+ /**
* Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request.
* @param urlTemplate a URL template; the resulting URL will be encoded
- * @param urlVariables zero or more URL variables
+ * @param urlVars zero or more URL variables
*/
- public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) {
- return new MockMultipartHttpServletRequestBuilder(urlTemplate, urlVariables);
+ public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVars) {
+ return new MockMultipartHttpServletRequestBuilder(urlTemplate, urlVars);
}
/**
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/HandlerResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/HandlerResultMatchers.java
index 982eacdf..211ca9a7 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/HandlerResultMatchers.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/HandlerResultMatchers.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -24,22 +24,33 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.util.ClassUtils;
import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.MethodInvocationInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
-import static org.hamcrest.MatcherAssert.*;
-import static org.springframework.test.util.AssertionErrors.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.springframework.test.util.AssertionErrors.assertEquals;
+import static org.springframework.test.util.AssertionErrors.assertTrue;
+import static org.springframework.test.util.AssertionErrors.fail;
/**
- * Factory for assertions on the selected handler.
+ * Factory for assertions on the selected handler or handler method.
* <p>An instance of this class is typically accessed via
* {@link MockMvcResultMatchers#handler}.
*
+ * <p><strong>Note:</strong> Expectations that assert the controller method
+ * used to process the request work only for requests processed with
+ * {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}
+ * which is used by default with the Spring MVC Java config and XML namespace.
+ *
* @author Rossen Stoyanchev
+ * @author Sam Brannen
* @since 3.2
*/
public class HandlerResultMatchers {
+
/**
* Protected constructor.
* Use {@link MockMvcResultMatchers#handler()}.
@@ -67,56 +78,92 @@ public class HandlerResultMatchers {
}
/**
- * Assert the name of the controller method that processed the request with
- * the given Hamcrest {@link Matcher}.
- * <p>Use of this method implies annotated controllers are processed with
- * {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
+ * Assert the controller method used to process the request.
+ * <p>The expected method is specified through a "mock" controller method
+ * invocation similar to {@link MvcUriComponentsBuilder#fromMethodCall(Object)}.
+ * <p>For example, given this controller:
+ * <pre class="code">
+ * &#064;RestController
+ * public class SimpleController {
+ *
+ * &#064;RequestMapping("/")
+ * public ResponseEntity<Void> handle() {
+ * return ResponseEntity.ok().build();
+ * }
+ * }
+ * </pre>
+ * <p>A test that has statically imported {@link MvcUriComponentsBuilder#on}
+ * can be performed as follows:
+ * <pre class="code">
+ * mockMvc.perform(get("/"))
+ * .andExpect(handler().methodCall(on(SimpleController.class).handle()));
+ * </pre>
+ *
+ * @param obj either the value returned from a "mock" controller invocation
+ * or the "mock" controller itself after an invocation
+ */
+ public ResultMatcher methodCall(final Object obj) {
+ return new ResultMatcher() {
+ @Override
+ public void match(MvcResult result) throws Exception {
+ if (!MethodInvocationInfo.class.isInstance(obj)) {
+ fail(String.format("The supplied object [%s] is not an instance of %s. "
+ + "Ensure that you invoke the handler method via MvcUriComponentsBuilder.on().",
+ obj, MethodInvocationInfo.class.getName()));
+ }
+ MethodInvocationInfo invocationInfo = (MethodInvocationInfo) obj;
+ Method expected = invocationInfo.getControllerMethod();
+ Method actual = getHandlerMethod(result).getMethod();
+ assertEquals("Handler method", expected, actual);
+ }
+ };
+ }
+
+ /**
+ * Assert the name of the controller method used to process the request
+ * using the given Hamcrest {@link Matcher}.
*/
public ResultMatcher methodName(final Matcher<? super String> matcher) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- Object handler = assertHandlerMethod(result);
- assertThat("HandlerMethod", ((HandlerMethod) handler).getMethod().getName(), matcher);
+ HandlerMethod handlerMethod = getHandlerMethod(result);
+ assertThat("Handler method", handlerMethod.getMethod().getName(), matcher);
}
};
}
/**
- * Assert the name of the controller method that processed the request.
- * <p>Use of this method implies annotated controllers are processed with
- * {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
+ * Assert the name of the controller method used to process the request.
*/
public ResultMatcher methodName(final String name) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- Object handler = assertHandlerMethod(result);
- assertEquals("HandlerMethod", name, ((HandlerMethod) handler).getMethod().getName());
+ HandlerMethod handlerMethod = getHandlerMethod(result);
+ assertEquals("Handler method", name, handlerMethod.getMethod().getName());
}
};
}
/**
- * Assert the controller method that processed the request.
- * <p>Use of this method implies annotated controllers are processed with
- * {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}.
+ * Assert the controller method used to process the request.
*/
public ResultMatcher method(final Method method) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- Object handler = assertHandlerMethod(result);
- assertEquals("HandlerMethod", method, ((HandlerMethod) handler).getMethod());
+ HandlerMethod handlerMethod = getHandlerMethod(result);
+ assertEquals("Handler method", method, handlerMethod.getMethod());
}
};
}
- private static Object assertHandlerMethod(MvcResult result) {
+ private static HandlerMethod getHandlerMethod(MvcResult result) {
Object handler = result.getHandler();
assertTrue("No handler: ", handler != null);
assertTrue("Not a HandlerMethod: " + handler, HandlerMethod.class.isInstance(handler));
- return handler;
+ return (HandlerMethod) handler;
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java
index c57387ee..aac0bab9 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,22 +16,26 @@
package org.springframework.test.web.servlet.result;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
import org.hamcrest.Matcher;
+import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
-import static org.hamcrest.MatcherAssert.*;
-import static org.springframework.test.util.AssertionErrors.*;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.springframework.test.util.AssertionErrors.assertEquals;
+import static org.springframework.test.util.AssertionErrors.assertTrue;
/**
* Factory for response header assertions.
- * <p>An instance of this class is usually accessed via
+ * <p>An instance of this class is available via
* {@link MockMvcResultMatchers#header}.
*
* @author Rossen Stoyanchev
@@ -41,16 +45,18 @@ import java.util.TimeZone;
*/
public class HeaderResultMatchers {
+
/**
* Protected constructor.
- * Use {@link MockMvcResultMatchers#header()}.
+ * See {@link MockMvcResultMatchers#header()}.
*/
protected HeaderResultMatchers() {
}
+
/**
- * Assert the primary value of the named response header with the given
- * Hamcrest {@link Matcher}.
+ * Assert the primary value of the response header with the given Hamcrest
+ * String {@code Matcher}.
*/
public ResultMatcher string(final String name, final Matcher<? super String> matcher) {
return new ResultMatcher() {
@@ -62,7 +68,22 @@ public class HeaderResultMatchers {
}
/**
- * Assert the primary value of the named response header as a {@link String}.
+ * Assert the values of the response header with the given Hamcrest
+ * Iterable {@link Matcher}.
+ * @since 4.3
+ */
+ public <T> ResultMatcher stringValues(final String name, final Matcher<Iterable<String>> matcher) {
+ return new ResultMatcher() {
+ @Override
+ public void match(MvcResult result) {
+ List<String> values = result.getResponse().getHeaders(name);
+ assertThat("Response header " + name, values, matcher);
+ }
+ };
+ }
+
+ /**
+ * Assert the primary value of the response header as a String value.
*/
public ResultMatcher string(final String name, final String value) {
return new ResultMatcher() {
@@ -74,6 +95,20 @@ public class HeaderResultMatchers {
}
/**
+ * Assert the values of the response header as String values.
+ * @since 4.3
+ */
+ public ResultMatcher stringValues(final String name, final String... values) {
+ return new ResultMatcher() {
+ @Override
+ public void match(MvcResult result) {
+ List<Object> actual = result.getResponse().getHeaderValues(name);
+ assertEquals("Response header " + name, Arrays.asList(values), actual);
+ }
+ };
+ }
+
+ /**
* Assert that the named response header does not exist.
* @since 4.0
*/
@@ -81,23 +116,25 @@ public class HeaderResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
- assertTrue("Response should not contain header " + name, !result.getResponse().containsHeader(name));
+ assertTrue("Response should not contain header " + name,
+ !result.getResponse().containsHeader(name));
}
};
}
/**
* Assert the primary value of the named response header as a {@code long}.
- * <p>The {@link ResultMatcher} returned by this method throws an {@link AssertionError}
- * if the response does not contain the specified header, or if the supplied
- * {@code value} does not match the primary value.
+ * <p>The {@link ResultMatcher} returned by this method throws an
+ * {@link AssertionError} if the response does not contain the specified
+ * header, or if the supplied {@code value} does not match the primary value.
*/
public ResultMatcher longValue(final String name, final long value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
- assertTrue("Response does not contain header " + name, result.getResponse().containsHeader(name));
- assertEquals("Response header " + name, value, Long.parseLong(result.getResponse().getHeader(name)));
+ MockHttpServletResponse response = result.getResponse();
+ assertTrue("Response does not contain header " + name, response.containsHeader(name));
+ assertEquals("Response header " + name, value, Long.parseLong(response.getHeader(name)));
}
};
}
@@ -105,10 +142,9 @@ public class HeaderResultMatchers {
/**
* Assert the primary value of the named response header as a date String,
* using the preferred date format described in RFC 7231.
- * <p>The {@link ResultMatcher} returned by this method throws an {@link AssertionError}
- * if the response does not contain the specified header, or if the supplied
- * {@code value} does not match the primary value.
- *
+ * <p>The {@link ResultMatcher} returned by this method throws an
+ * {@link AssertionError} if the response does not contain the specified
+ * header, or if the supplied {@code value} does not match the primary value.
* @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
* @since 4.2
*/
@@ -118,8 +154,10 @@ public class HeaderResultMatchers {
public void match(MvcResult result) {
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
- assertTrue("Response does not contain header " + name, result.getResponse().containsHeader(name));
- assertEquals("Response header " + name, format.format(new Date(value)), result.getResponse().getHeader(name));
+ String formatted = format.format(new Date(value));
+ MockHttpServletResponse response = result.getResponse();
+ assertTrue("Response does not contain header " + name, response.containsHeader(name));
+ assertEquals("Response header " + name, formatted, response.getHeader(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 39377075..f572a372 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
@@ -16,12 +16,17 @@
package org.springframework.test.web.servlet.result;
+import java.io.UnsupportedEncodingException;
+
import com.jayway.jsonpath.JsonPath;
import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.core.StringStartsWith;
import org.springframework.test.util.JsonPathExpectationsHelper;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
+import org.springframework.util.StringUtils;
/**
* Factory for assertions on the response content using
@@ -33,12 +38,15 @@ import org.springframework.test.web.servlet.ResultMatcher;
* @author Rossen Stoyanchev
* @author Craig Andrews
* @author Sam Brannen
+ * @author Brian Clozel
* @since 3.2
*/
public class JsonPathResultMatchers {
private final JsonPathExpectationsHelper jsonPathHelper;
+ private String prefix;
+
/**
* Protected constructor.
@@ -52,6 +60,19 @@ public class JsonPathResultMatchers {
this.jsonPathHelper = new JsonPathExpectationsHelper(expression, args);
}
+ /**
+ * Configures the current {@code JsonPathResultMatchers} instance
+ * to verify that the JSON payload is prepended with the given prefix.
+ * <p>Use this method if the JSON payloads are prefixed to avoid
+ * Cross Site Script Inclusion (XSSI) attacks.
+ * @param prefix the string prefix prepended to the actual JSON payload
+ * @since 4.3
+ */
+ public JsonPathResultMatchers prefix(String prefix) {
+ this.prefix = prefix;
+ return this;
+ }
+
/**
* Evaluate the JSON path expression against the response content and
@@ -61,7 +82,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValue(content, matcher);
}
};
@@ -75,7 +96,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- jsonPathHelper.assertValue(result.getResponse().getContentAsString(), expectedValue);
+ jsonPathHelper.assertValue(getContent(result), expectedValue);
}
};
}
@@ -91,7 +112,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.exists(content);
}
};
@@ -108,7 +129,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.doesNotExist(content);
}
};
@@ -128,7 +149,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValueIsEmpty(content);
}
};
@@ -148,7 +169,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValueIsNotEmpty(content);
}
};
@@ -163,7 +184,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValueIsString(content);
}
};
@@ -178,7 +199,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValueIsBoolean(content);
}
};
@@ -193,7 +214,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValueIsNumber(content);
}
};
@@ -207,7 +228,7 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValueIsArray(content);
}
};
@@ -222,10 +243,29 @@ public class JsonPathResultMatchers {
return new ResultMatcher() {
@Override
public void match(MvcResult result) throws Exception {
- String content = result.getResponse().getContentAsString();
+ String content = getContent(result);
jsonPathHelper.assertValueIsMap(content);
}
};
}
+ private String getContent(MvcResult result) throws UnsupportedEncodingException {
+ String content = result.getResponse().getContentAsString();
+ if (StringUtils.hasLength(this.prefix)) {
+ try {
+ String reason = String.format("Expected a JSON payload prefixed with \"%s\" but found: %s",
+ this.prefix, StringUtils.quote(content.substring(0, this.prefix.length())));
+ MatcherAssert.assertThat(reason, content, StringStartsWith.startsWith(this.prefix));
+ return content.substring(this.prefix.length());
+ }
+ catch (StringIndexOutOfBoundsException oobe) {
+ throw new AssertionError(
+ "JSON prefix \"" + this.prefix + "\" not found, exception: " + oobe.getMessage());
+ }
+ }
+ else {
+ return content;
+ }
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultMatchers.java
index 32ad49fd..a12baf94 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultMatchers.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/MockMvcResultMatchers.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java
index 5efa9100..f2eba236 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/StatusResultMatchers.java
@@ -33,6 +33,7 @@ import static org.springframework.test.util.AssertionErrors.*;
* @author Keesun Baik
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
+ * @author Brian Clozel
* @since 3.2
*/
public class StatusResultMatchers {
@@ -562,6 +563,14 @@ public class StatusResultMatchers {
}
/**
+ * Assert the response status code is {@code HttpStatus.UNAVAILABLE_FOR_LEGAL_REASONS} (451).
+ * @since 4.3
+ */
+ public ResultMatcher isUnavailableForLegalReasons() {
+ return matcher(HttpStatus.valueOf(451));
+ }
+
+ /**
* Assert the response status code is {@code HttpStatus.INTERNAL_SERVER_ERROR} (500).
*/
public ResultMatcher isInternalServerError() {
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java
index 21dd9ebb..50c4db84 100644
--- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -55,7 +55,7 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
private final List<ResultHandler> globalResultHandlers = new ArrayList<ResultHandler>();
- private Boolean dispatchOptions = Boolean.FALSE;
+ private Boolean dispatchOptions = Boolean.TRUE;
private final List<MockMvcConfigurer> configurers = new ArrayList<MockMvcConfigurer>(4);
diff --git a/spring-test/src/main/resources/META-INF/spring.factories b/spring-test/src/main/resources/META-INF/spring.factories
index 012b38df..30cd85a1 100644
--- a/spring-test/src/main/resources/META-INF/spring.factories
+++ b/spring-test/src/main/resources/META-INF/spring.factories
@@ -7,3 +7,8 @@ org.springframework.test.context.TestExecutionListener = \
org.springframework.test.context.support.DirtiesContextTestExecutionListener,\
org.springframework.test.context.transaction.TransactionalTestExecutionListener,\
org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
+
+# Default ContextCustomizerFactory implementations for the Spring TestContext Framework
+#
+org.springframework.test.context.ContextCustomizerFactory = \
+ org.springframework.test.context.web.socket.MockServerContainerContextCustomizerFactory
diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java
index bbafd498..d008751f 100644
--- a/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java
+++ b/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java
@@ -83,7 +83,7 @@ public class MockFilterChainTests {
chain.doFilter(this.request, this.response);
fail("Expected Exception");
}
- catch(IllegalStateException ex) {
+ catch (IllegalStateException ex) {
assertEquals("This FilterChain has already been called!", ex.getMessage());
}
}
@@ -98,7 +98,7 @@ public class MockFilterChainTests {
chain.doFilter(this.request, this.response);
fail("Expected Exception");
}
- catch(IllegalStateException ex) {
+ catch (IllegalStateException ex) {
assertEquals("This FilterChain has already been called!", ex.getMessage());
}
}
@@ -122,7 +122,7 @@ public class MockFilterChainTests {
chain.doFilter(this.request, this.response);
fail("Expected Exception");
}
- catch(IllegalStateException ex) {
+ catch (IllegalStateException ex) {
assertEquals("This FilterChain has already been called!", ex.getMessage());
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java b/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java
index 4296fd07..da461bee 100644
--- a/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -91,7 +91,10 @@ public class ProfileValueUtilsTests {
assertClassIsEnabled(EnabledAnnotatedMultiValue.class);
assertClassIsEnabled(MetaEnabledClass.class);
assertClassIsEnabled(MetaEnabledWithCustomProfileValueSourceClass.class);
+ assertClassIsEnabled(EnabledWithCustomProfileValueSourceOnTestInterface.class);
+
assertClassIsDisabled(DisabledAnnotatedSingleValue.class);
+ assertClassIsDisabled(DisabledAnnotatedSingleValueOnTestInterface.class);
assertClassIsDisabled(DisabledAnnotatedMultiValue.class);
assertClassIsDisabled(MetaDisabledClass.class);
assertClassIsDisabled(MetaDisabledWithCustomProfileValueSourceClass.class);
@@ -105,6 +108,7 @@ public class ProfileValueUtilsTests {
assertMethodIsEnabled(ENABLED_ANNOTATED_METHOD, EnabledAnnotatedSingleValue.class);
assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, EnabledAnnotatedSingleValue.class);
+
assertMethodIsEnabled(NON_ANNOTATED_METHOD, MetaEnabledAnnotatedSingleValue.class);
assertMethodIsEnabled(ENABLED_ANNOTATED_METHOD, MetaEnabledAnnotatedSingleValue.class);
assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, MetaEnabledAnnotatedSingleValue.class);
@@ -117,6 +121,8 @@ public class ProfileValueUtilsTests {
assertMethodIsDisabled(ENABLED_ANNOTATED_METHOD, DisabledAnnotatedSingleValue.class);
assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, DisabledAnnotatedSingleValue.class);
+ assertMethodIsDisabled(NON_ANNOTATED_METHOD, DisabledAnnotatedSingleValueOnTestInterface.class);
+
assertMethodIsDisabled(NON_ANNOTATED_METHOD, MetaDisabledAnnotatedSingleValue.class);
assertMethodIsDisabled(ENABLED_ANNOTATED_METHOD, MetaDisabledAnnotatedSingleValue.class);
assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, MetaDisabledAnnotatedSingleValue.class);
@@ -176,6 +182,17 @@ public class ProfileValueUtilsTests {
}
}
+ @IfProfileValue(name = NAME, value = VALUE + "X")
+ private interface IfProfileValueTestInterface {
+ }
+
+ @SuppressWarnings("unused")
+ private static class DisabledAnnotatedSingleValueOnTestInterface implements IfProfileValueTestInterface {
+
+ public void nonAnnotatedMethod() {
+ }
+ }
+
@SuppressWarnings("unused")
@IfProfileValue(name = NAME, values = { "foo", VALUE, "bar" })
private static class EnabledAnnotatedMultiValue {
@@ -302,4 +319,13 @@ public class ProfileValueUtilsTests {
private static class MetaDisabledWithCustomProfileValueSourceClass {
}
+ @ProfileValueSourceConfiguration(HardCodedProfileValueSource.class)
+ private interface CustomProfileValueSourceTestInterface {
+ }
+
+ @IfProfileValue(name = NAME, value = "42")
+ private static class EnabledWithCustomProfileValueSourceOnTestInterface
+ implements CustomProfileValueSourceTestInterface {
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java
index 8efb56d3..70d80ee3 100644
--- a/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/BootstrapUtilsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -24,16 +24,20 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.context.web.WebTestContextBootstrapper;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-import static org.springframework.test.context.BootstrapUtils.*;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.springframework.test.context.BootstrapUtils.resolveTestContextBootstrapper;
/**
* Unit tests for {@link BootstrapUtils}.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 4.2
*/
public class BootstrapUtilsTests {
@@ -41,7 +45,7 @@ public class BootstrapUtilsTests {
private final CacheAwareContextLoaderDelegate delegate = mock(CacheAwareContextLoaderDelegate.class);
@Rule
- public ExpectedException exception = ExpectedException.none();
+ public final ExpectedException exception = ExpectedException.none();
@Test
public void resolveTestContextBootstrapperForNonAnnotatedClass() {
@@ -49,6 +53,11 @@ public class BootstrapUtilsTests {
}
@Test
+ public void resolveTestContextBootstrapperForWebAppConfigurationAnnotatedClass() {
+ assertBootstrapper(WebAppConfigurationAnnotatedClass.class, WebTestContextBootstrapper.class);
+ }
+
+ @Test
public void resolveTestContextBootstrapperWithEmptyBootstrapWithAnnotation() {
BootstrapContext bootstrapContext = BootstrapTestUtils.buildBootstrapContext(EmptyBootstrapWithAnnotationClass.class, delegate);
@@ -124,4 +133,7 @@ public class BootstrapUtilsTests {
@BootWithFoo
static class DoubleMetaAnnotatedBootstrapWithAnnotationClass {}
+ @WebAppConfiguration
+ static class WebAppConfigurationAnnotatedClass {}
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java b/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java
index bf8eba04..0409e11f 100644
--- a/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,6 +16,7 @@
package org.springframework.test.context;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -28,6 +29,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.GenericXmlContextLoader;
import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
/**
* Unit tests for {@link MergedContextConfiguration}.
@@ -37,6 +39,7 @@ import static org.junit.Assert.*;
* {@link org.springframework.test.context.cache.ContextCache ContextCache}.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 3.1
*/
public class MergedContextConfigurationTests {
@@ -401,6 +404,35 @@ public class MergedContextConfigurationTests {
}
/**
+ * @since 4.3
+ */
+ @Test
+ public void equalsWithSameContextCustomizers() {
+ Set<ContextCustomizer> customizers = Collections.singleton(mock(ContextCustomizer.class));
+ MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
+ EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers, loader, null, null);
+ MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
+ EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers, loader, null, null);
+ assertEquals(mergedConfig1, mergedConfig2);
+ }
+
+ /**
+ * @since 4.3
+ */
+ @Test
+ public void equalsWithDifferentContextCustomizers() {
+ Set<ContextCustomizer> customizers1 = Collections.singleton(mock(ContextCustomizer.class));
+ Set<ContextCustomizer> customizers2 = Collections.singleton(mock(ContextCustomizer.class));
+
+ MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
+ EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers1, loader, null, null);
+ MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
+ EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers2, loader, null, null);
+ assertNotEquals(mergedConfig1, mergedConfig2);
+ assertNotEquals(mergedConfig2, mergedConfig1);
+ }
+
+ /**
* @since 3.2.2
*/
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java
index 648655fb..4df53d82 100644
--- a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java
@@ -116,6 +116,12 @@ public class TestExecutionListenersTests {
}
@Test
+ public void customListenersDeclaredOnInterface() {
+ assertRegisteredListeners(ExplicitListenersOnTestInterfaceTestCase.class,
+ asList(FooTestExecutionListener.class, BarTestExecutionListener.class));
+ }
+
+ @Test
public void nonInheritedListeners() {
assertNumRegisteredListeners(NonInheritedListenersTestCase.class, 1);
}
@@ -229,6 +235,13 @@ public class TestExecutionListenersTests {
static class NonInheritedListenersTestCase extends InheritedListenersTestCase {
}
+ @TestExecutionListeners({ FooTestExecutionListener.class, BarTestExecutionListener.class })
+ interface ExplicitListenersTestInterface {
+ }
+
+ static class ExplicitListenersOnTestInterfaceTestCase implements ExplicitListenersTestInterface {
+ }
+
@TestExecutionListeners(listeners = FooTestExecutionListener.class, value = BarTestExecutionListener.class)
static class DuplicateListenersConfigTestCase {
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTestNGTests.java b/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTestNGTests.java
index 2fcbdb46..1c603e33 100644
--- a/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTestNGTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTestNGTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -171,7 +171,7 @@ public class ClassLevelDirtiesContextTestNGTests {
@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class }, inheritListeners = false)
@ContextConfiguration
- public static abstract class BaseTestCase extends AbstractTestNGSpringContextTests {
+ static abstract class BaseTestCase extends AbstractTestNGSpringContextTests {
@Configuration
static class Config {
@@ -189,75 +189,75 @@ public class ClassLevelDirtiesContextTestNGTests {
}
}
- public static final class CleanTestCase extends BaseTestCase {
+ static final class CleanTestCase extends BaseTestCase {
@org.testng.annotations.Test
- public void verifyContextWasAutowired() {
+ void verifyContextWasAutowired() {
assertApplicationContextWasAutowired();
}
}
@DirtiesContext
- public static class ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase extends BaseTestCase {
+ static class ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase extends BaseTestCase {
@org.testng.annotations.Test
- public void verifyContextWasAutowired() {
+ void verifyContextWasAutowired() {
assertApplicationContextWasAutowired();
}
}
- public static class InheritedClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase extends
+ static class InheritedClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase extends
ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase {
}
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
- public static class ClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase extends BaseTestCase {
+ static class ClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase extends BaseTestCase {
@org.testng.annotations.Test
- public void verifyContextWasAutowired() {
+ void verifyContextWasAutowired() {
assertApplicationContextWasAutowired();
}
}
- public static class InheritedClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase extends
+ static class InheritedClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase extends
ClassLevelDirtiesContextWithCleanMethodsAndAfterClassModeTestCase {
}
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
- public static class ClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase extends BaseTestCase {
+ static class ClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase extends BaseTestCase {
@org.testng.annotations.Test
- public void verifyContextWasAutowired1() {
+ void verifyContextWasAutowired1() {
assertApplicationContextWasAutowired();
}
@org.testng.annotations.Test
- public void verifyContextWasAutowired2() {
+ void verifyContextWasAutowired2() {
assertApplicationContextWasAutowired();
}
@org.testng.annotations.Test
- public void verifyContextWasAutowired3() {
+ void verifyContextWasAutowired3() {
assertApplicationContextWasAutowired();
}
}
- public static class InheritedClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase extends
+ static class InheritedClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase extends
ClassLevelDirtiesContextWithAfterEachTestMethodModeTestCase {
}
@DirtiesContext
- public static class ClassLevelDirtiesContextWithDirtyMethodsTestCase extends BaseTestCase {
+ static class ClassLevelDirtiesContextWithDirtyMethodsTestCase extends BaseTestCase {
@org.testng.annotations.Test
@DirtiesContext
- public void dirtyContext() {
+ void dirtyContext() {
assertApplicationContextWasAutowired();
}
}
- public static class InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase extends
+ static class InheritedClassLevelDirtiesContextWithDirtyMethodsTestCase extends
ClassLevelDirtiesContextWithDirtyMethodsTestCase {
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java b/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java
index 896c4654..9da13e7b 100644
--- a/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -30,10 +30,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.TestExecutionListeners;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
-import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
+import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.*;
import static org.springframework.test.context.cache.ContextCacheTestUtils.*;
@@ -41,9 +38,8 @@ import static org.springframework.test.context.junit4.JUnitTestingUtils.*;
/**
* JUnit 4 based integration test which verifies correct {@linkplain ContextCache
- * application context caching} in conjunction with the
- * {@link SpringJUnit4ClassRunner} and {@link DirtiesContext @DirtiesContext}
- * at the class level.
+ * application context caching} in conjunction with the {@link SpringRunner} and
+ * {@link DirtiesContext @DirtiesContext} at the class level.
*
* @author Sam Brannen
* @since 3.0
@@ -148,10 +144,9 @@ public class ClassLevelDirtiesContextTests {
// -------------------------------------------------------------------
- @RunWith(SpringJUnit4ClassRunner.class)
- @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
+ @RunWith(SpringRunner.class)
@ContextConfiguration
- public static abstract class BaseTestCase {
+ static abstract class BaseTestCase {
@Configuration
static class Config {
diff --git a/spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheTests.java
index 5d518d3c..4624fcb7 100644
--- a/spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -42,6 +42,7 @@ import static org.springframework.test.context.cache.ContextCacheTestUtils.*;
* @author Sam Brannen
* @author Michail Nikolaev
* @since 3.1
+ * @see LruContextCacheTests
* @see SpringRunnerContextCacheTests
*/
public class ContextCacheTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheUtilsTests.java
new file mode 100644
index 00000000..ac6cc723
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/cache/ContextCacheUtilsTests.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2002-2016 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.context.cache;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.core.SpringProperties;
+
+import static org.junit.Assert.*;
+import static org.springframework.test.context.cache.ContextCacheUtils.*;
+import static org.springframework.test.context.cache.ContextCache.*;
+
+/**
+ * Unit tests for {@link ContextCacheUtils}.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ */
+public class ContextCacheUtilsTests {
+
+ @Before
+ @After
+ public void clearProperties() {
+ System.clearProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME);
+ SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, null);
+ }
+
+ @Test
+ public void retrieveMaxCacheSizeFromDefault() {
+ assertDefaultValue();
+ }
+
+ @Test
+ public void retrieveMaxCacheSizeFromBogusSystemProperty() {
+ System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "bogus");
+ assertDefaultValue();
+ }
+
+ @Test
+ public void retrieveMaxCacheSizeFromBogusSpringProperty() {
+ SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "bogus");
+ assertDefaultValue();
+ }
+
+ @Test
+ public void retrieveMaxCacheSizeFromDecimalSpringProperty() {
+ SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "3.14");
+ assertDefaultValue();
+ }
+
+ @Test
+ public void retrieveMaxCacheSizeFromSystemProperty() {
+ System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "42");
+ assertEquals(42, retrieveMaxCacheSize());
+ }
+
+ @Test
+ public void retrieveMaxCacheSizeFromSystemPropertyContainingWhitespace() {
+ System.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "42\t");
+ assertEquals(42, retrieveMaxCacheSize());
+ }
+
+ @Test
+ public void retrieveMaxCacheSizeFromSpringProperty() {
+ SpringProperties.setProperty(MAX_CONTEXT_CACHE_SIZE_PROPERTY_NAME, "99");
+ assertEquals(99, retrieveMaxCacheSize());
+ }
+
+ private static void assertDefaultValue() {
+ assertEquals(DEFAULT_MAX_CONTEXT_CACHE_SIZE, retrieveMaxCacheSize());
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/cache/LruContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/cache/LruContextCacheTests.java
new file mode 100644
index 00000000..4ae97b61
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/cache/LruContextCacheTests.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2002-2016 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.context.cache;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static java.util.Arrays.*;
+import static java.util.stream.Collectors.*;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * Unit tests for the LRU eviction policy in {@link DefaultContextCache}.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ * @see ContextCacheTests
+ */
+public class LruContextCacheTests {
+
+ private static final MergedContextConfiguration abcConfig = config(Abc.class);
+ private static final MergedContextConfiguration fooConfig = config(Foo.class);
+ private static final MergedContextConfiguration barConfig = config(Bar.class);
+ private static final MergedContextConfiguration bazConfig = config(Baz.class);
+
+
+ private final ConfigurableApplicationContext abcContext = mock(ConfigurableApplicationContext.class);
+ private final ConfigurableApplicationContext fooContext = mock(ConfigurableApplicationContext.class);
+ private final ConfigurableApplicationContext barContext = mock(ConfigurableApplicationContext.class);
+ private final ConfigurableApplicationContext bazContext = mock(ConfigurableApplicationContext.class);
+
+
+ @Test(expected = IllegalArgumentException.class)
+ public void maxCacheSizeNegativeOne() {
+ new DefaultContextCache(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void maxCacheSizeZero() {
+ new DefaultContextCache(0);
+ }
+
+ @Test
+ public void maxCacheSizeOne() {
+ DefaultContextCache cache = new DefaultContextCache(1);
+ assertEquals(0, cache.size());
+ assertEquals(1, cache.getMaxSize());
+
+ cache.put(fooConfig, fooContext);
+ assertCacheContents(cache, "Foo");
+
+ cache.put(fooConfig, fooContext);
+ assertCacheContents(cache, "Foo");
+
+ cache.put(barConfig, barContext);
+ assertCacheContents(cache, "Bar");
+
+ cache.put(fooConfig, fooContext);
+ assertCacheContents(cache, "Foo");
+ }
+
+ @Test
+ public void maxCacheSizeThree() {
+ DefaultContextCache cache = new DefaultContextCache(3);
+ assertEquals(0, cache.size());
+ assertEquals(3, cache.getMaxSize());
+
+ cache.put(fooConfig, fooContext);
+ assertCacheContents(cache, "Foo");
+
+ cache.put(fooConfig, fooContext);
+ assertCacheContents(cache, "Foo");
+
+ cache.put(barConfig, barContext);
+ assertCacheContents(cache, "Foo", "Bar");
+
+ cache.put(bazConfig, bazContext);
+ assertCacheContents(cache, "Foo", "Bar", "Baz");
+
+ cache.put(abcConfig, abcContext);
+ assertCacheContents(cache, "Bar", "Baz", "Abc");
+ }
+
+ @Test
+ public void ensureLruOrderingIsUpdated() {
+ DefaultContextCache cache = new DefaultContextCache(3);
+
+ // Note: when a new entry is added it is considered the MRU entry and inserted at the tail.
+ cache.put(fooConfig, fooContext);
+ cache.put(barConfig, barContext);
+ cache.put(bazConfig, bazContext);
+ assertCacheContents(cache, "Foo", "Bar", "Baz");
+
+ // Note: the MRU entry is moved to the tail when accessed.
+ cache.get(fooConfig);
+ assertCacheContents(cache, "Bar", "Baz", "Foo");
+
+ cache.get(barConfig);
+ assertCacheContents(cache, "Baz", "Foo", "Bar");
+
+ cache.get(bazConfig);
+ assertCacheContents(cache, "Foo", "Bar", "Baz");
+
+ cache.get(barConfig);
+ assertCacheContents(cache, "Foo", "Baz", "Bar");
+ }
+
+ @Test
+ public void ensureEvictedContextsAreClosed() {
+ DefaultContextCache cache = new DefaultContextCache(2);
+
+ cache.put(fooConfig, fooContext);
+ cache.put(barConfig, barContext);
+ assertCacheContents(cache, "Foo", "Bar");
+
+ cache.put(bazConfig, bazContext);
+ assertCacheContents(cache, "Bar", "Baz");
+ verify(fooContext, times(1)).close();
+
+ cache.put(abcConfig, abcContext);
+ assertCacheContents(cache, "Baz", "Abc");
+ verify(barContext, times(1)).close();
+
+ verify(abcContext, never()).close();
+ verify(bazContext, never()).close();
+ }
+
+
+ private static MergedContextConfiguration config(Class<?> clazz) {
+ return new MergedContextConfiguration(null, null, new Class<?>[] { clazz }, null, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void assertCacheContents(DefaultContextCache cache, String... expectedNames) {
+
+ Map<MergedContextConfiguration, ApplicationContext> contextMap =
+ (Map<MergedContextConfiguration, ApplicationContext>) ReflectionTestUtils.getField(cache, "contextMap");
+
+ // @formatter:off
+ List<String> actualNames = contextMap.keySet().stream()
+ .map(cfg -> cfg.getClasses()[0])
+ .map(Class::getSimpleName)
+ .collect(toList());
+ // @formatter:on
+
+ assertEquals(asList(expectedNames), actualNames);
+ }
+
+
+ private static class Abc {}
+ private static class Foo {}
+ private static class Bar {}
+ private static class Baz {}
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/cache/SpringRunnerContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/cache/SpringRunnerContextCacheTests.java
index 44962e76..bde994dc 100644
--- a/spring-test/src/test/java/org/springframework/test/context/cache/SpringRunnerContextCacheTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/cache/SpringRunnerContextCacheTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -45,6 +45,7 @@ import static org.springframework.test.context.cache.ContextCacheTestUtils.*;
* @author Juergen Hoeller
* @since 2.5
* @see ContextCacheTests
+ * @see LruContextCacheTests
*/
@RunWith(SpringJUnit4ClassRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java
new file mode 100644
index 00000000..80d33c8d
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.tests.sample.beans.Employee;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+public class ActiveProfilesInterfaceTests implements ActiveProfilesTestInterface {
+
+ @Autowired
+ Employee employee;
+
+
+ @Test
+ public void profileFromTestInterface() {
+ assertNotNull(employee);
+ assertEquals("dev", employee.getName());
+ }
+
+
+ @Configuration
+ static class Config {
+
+ @Bean
+ @Profile("dev")
+ Employee employee1() {
+ return new Employee("dev");
+ }
+
+ @Bean
+ @Profile("prod")
+ Employee employee2() {
+ return new Employee("prod");
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java
new file mode 100644
index 00000000..63ef2004
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.springframework.test.context.ActiveProfiles;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@ActiveProfiles("dev")
+interface ActiveProfilesTestInterface {
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java
new file mode 100644
index 00000000..47e52b9f
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+public class BootstrapWithInterfaceTests implements BootstrapWithTestInterface {
+
+ @Autowired
+ String foo;
+
+
+ @Test
+ public void injectedBean() {
+ assertEquals("foo", foo);
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java
new file mode 100644
index 00000000..993f3afa
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import java.util.List;
+
+import org.springframework.test.context.BootstrapWith;
+import org.springframework.test.context.ContextCustomizer;
+import org.springframework.test.context.ContextCustomizerFactory;
+import org.springframework.test.context.configuration.interfaces.BootstrapWithTestInterface.CustomTestContextBootstrapper;
+import org.springframework.test.context.support.DefaultTestContextBootstrapper;
+
+import static java.util.Collections.*;
+
+/**
+ * @author Sam Brannen
+ * @author Phillip Webb
+ * @since 4.3
+ */
+@BootstrapWith(CustomTestContextBootstrapper.class)
+interface BootstrapWithTestInterface {
+
+ static class CustomTestContextBootstrapper extends DefaultTestContextBootstrapper {
+
+ @Override
+ protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
+ return singletonList(
+ (ContextCustomizerFactory) (testClass, configAttributes) -> (ContextCustomizer) (context,
+ mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "foo"));
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java
new file mode 100644
index 00000000..4a035e77
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.tests.sample.beans.Employee;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+public class ContextConfigurationInterfaceTests implements ContextConfigurationTestInterface {
+
+ @Autowired
+ Employee employee;
+
+
+ @Test
+ public void profileFromTestInterface() {
+ assertNotNull(employee);
+ assertEquals("Dilbert", employee.getName());
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java
new file mode 100644
index 00000000..7eb452fc
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.configuration.interfaces.ContextConfigurationTestInterface.Config;
+import org.springframework.tests.sample.beans.Employee;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@ContextConfiguration(classes = Config.class)
+interface ContextConfigurationTestInterface {
+
+ static class Config {
+
+ @Bean
+ Employee employee() {
+ return new Employee("Dilbert");
+ }
+ }
+
+} \ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java
new file mode 100644
index 00000000..832c244b
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+public class ContextHierarchyInterfaceTests implements ContextHierarchyTestInterface {
+
+ @Autowired
+ String foo;
+
+ @Autowired
+ String bar;
+
+ @Autowired
+ String baz;
+
+ @Autowired
+ ApplicationContext context;
+
+
+ @Test
+ public void loadContextHierarchy() {
+ assertNotNull("child ApplicationContext", context);
+ assertNotNull("parent ApplicationContext", context.getParent());
+ assertNull("grandparent ApplicationContext", context.getParent().getParent());
+ assertEquals("foo", foo);
+ assertEquals("bar", bar);
+ assertEquals("baz-child", baz);
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java
new file mode 100644
index 00000000..538e2a93
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.ContextHierarchy;
+import org.springframework.test.context.hierarchies.standard.SingleTestClassWithTwoLevelContextHierarchyTests;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@ContextHierarchy({
+ @ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ParentConfig.class),
+ @ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ChildConfig.class) })
+interface ContextHierarchyTestInterface {
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java
new file mode 100644
index 00000000..6b0be0a1
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.*;
+import static org.springframework.test.context.cache.ContextCacheTestUtils.*;
+import static org.springframework.test.context.junit4.JUnitTestingUtils.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(JUnit4.class)
+public class DirtiesContextInterfaceTests {
+
+ private static final AtomicInteger cacheHits = new AtomicInteger(0);
+ private static final AtomicInteger cacheMisses = new AtomicInteger(0);
+
+
+ @BeforeClass
+ public static void verifyInitialCacheState() {
+ resetContextCache();
+ // Reset static counters in case tests are run multiple times in a test suite --
+ // for example, via JUnit's @Suite.
+ cacheHits.set(0);
+ cacheMisses.set(0);
+ assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
+ }
+
+ @AfterClass
+ public static void verifyFinalCacheState() {
+ assertContextCacheStatistics("AfterClass", 0, cacheHits.get(), cacheMisses.get());
+ }
+
+ @Test
+ public void verifyDirtiesContextBehavior() throws Exception {
+ runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase.class, 1);
+ assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and default class mode",
+ 0, cacheHits.get(), cacheMisses.incrementAndGet());
+ }
+
+ private void runTestClassAndAssertStats(Class<?> testClass, int expectedTestCount) throws Exception {
+ runTestsAndAssertCounters(testClass, expectedTestCount, 0, expectedTestCount, 0, 0);
+ }
+
+
+ @RunWith(SpringRunner.class)
+ public static class ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase
+ implements DirtiesContextTestInterface {
+
+ @Autowired
+ ApplicationContext applicationContext;
+
+
+ @Test
+ public void verifyContextWasAutowired() {
+ assertNotNull("The application context should have been autowired.", this.applicationContext);
+ }
+
+
+ @Configuration
+ static class Config {
+ /* no beans */
+ }
+
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java
new file mode 100644
index 00000000..cd486e14
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@DirtiesContext
+interface DirtiesContextTestInterface {
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java
new file mode 100644
index 00000000..26f044f8
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.junit.Test;
+
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.jdbc.SqlConfig;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+public class SqlConfigInterfaceTests extends AbstractTransactionalJUnit4SpringContextTests
+ implements SqlConfigTestInterface {
+
+ @Test
+ @Sql(scripts = "/org/springframework/test/context/jdbc/schema.sql", //
+ config = @SqlConfig(separator = ";"))
+ @Sql("/org/springframework/test/context/jdbc/data-add-users-with-custom-script-syntax.sql")
+ public void methodLevelScripts() {
+ assertNumUsers(3);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java
new file mode 100644
index 00000000..a707a4c2
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.jdbc.EmptyDatabaseConfig;
+import org.springframework.test.context.jdbc.SqlConfig;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DirtiesContext
+@SqlConfig(commentPrefix = "`", blockCommentStartDelimiter = "#$", blockCommentEndDelimiter = "$#", separator = "@@")
+interface SqlConfigTestInterface {
+} \ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java
new file mode 100644
index 00000000..66dd6057
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+public class TestPropertySourceInterfaceTests implements TestPropertySourceTestInterface {
+
+ @Autowired
+ Environment env;
+
+
+ @Test
+ public void propertiesAreAvailableInEnvironment() {
+ assertThat(property("foo"), is("bar"));
+ assertThat(property("enigma"), is("42"));
+ }
+
+ private String property(String key) {
+ return env.getProperty(key);
+ }
+
+
+ @Configuration
+ static class Config {
+ /* no user beans required for these tests */
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java
new file mode 100644
index 00000000..af27989e
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.springframework.test.context.TestPropertySource;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@TestPropertySource(properties = { "foo = bar", "enigma: 42" })
+interface TestPropertySourceTestInterface {
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java
new file mode 100644
index 00000000..fdb6706b
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+public class WebAppConfigurationInterfaceTests implements WebAppConfigurationTestInterface {
+
+ @Autowired
+ WebApplicationContext wac;
+
+
+ @Test
+ public void wacLoaded() {
+ assertNotNull(wac);
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java
new file mode 100644
index 00000000..dcfabd14
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2002-2016 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.context.configuration.interfaces;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.configuration.interfaces.WebAppConfigurationTestInterface.Config;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@WebAppConfiguration
+@ContextConfiguration(classes = Config.class)
+interface WebAppConfigurationTestInterface {
+
+ @Configuration
+ static class Config {
+ /* no user beans required for these tests */
+ }
+
+} \ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesOverridePropertiesFilesTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesOverridePropertiesFilesTestPropertySourceTests.java
new file mode 100644
index 00000000..c50f7ba6
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesOverridePropertiesFilesTestPropertySourceTests.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2002-2016 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.context.env;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests for {@link TestPropertySource @TestPropertySource} support with
+ * inlined properties that overrides properties files.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@TestPropertySource(locations = "explicit.properties", properties = "explicit = inlined")
+public class InlinedPropertiesOverridePropertiesFilesTestPropertySourceTests {
+
+ @Autowired
+ Environment env;
+
+ @Value("${explicit}")
+ String explicit;
+
+
+ @Test
+ public void inlinedPropertyOverridesValueFromPropertiesFile() {
+ assertEquals("inlined", env.getProperty("explicit"));
+ assertEquals("inlined", this.explicit);
+ }
+
+
+ @Configuration
+ static class Config {
+
+ @Bean
+ public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
+ return new PropertySourcesPlaceholderConfigurer();
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java b/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java
index 2868b694..f8ba83fd 100644
--- a/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -40,7 +40,7 @@ import static org.junit.Assert.*;
public class SingleTestClassWithTwoLevelContextHierarchyTests {
@Configuration
- static class ParentConfig {
+ public static class ParentConfig {
@Bean
public String foo() {
@@ -54,7 +54,7 @@ public class SingleTestClassWithTwoLevelContextHierarchyTests {
}
@Configuration
- static class ChildConfig {
+ public static class ChildConfig {
@Bean
public String bar() {
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/ComposedAnnotationSqlScriptsTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/ComposedAnnotationSqlScriptsTests.java
new file mode 100644
index 00000000..0e709739
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/ComposedAnnotationSqlScriptsTests.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2002-2016 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.context.jdbc;
+
+import java.lang.annotation.Retention;
+
+import org.junit.Test;
+
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+
+import static java.lang.annotation.RetentionPolicy.*;
+import static org.junit.Assert.*;
+import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.*;
+
+/**
+ * Integration tests that verify support for using {@link Sql @Sql} as a
+ * merged, composed annotation.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DirtiesContext
+public class ComposedAnnotationSqlScriptsTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @Test
+ @ComposedSql(
+ scripts = { "drop-schema.sql", "schema.sql" },
+ statements = "INSERT INTO user VALUES('Dilbert')",
+ executionPhase = BEFORE_TEST_METHOD
+ )
+ public void composedSqlAnnotation() {
+ assertEquals("Number of rows in the 'user' table.", 1, countRowsInTable("user"));
+ }
+
+
+ @Sql
+ @Retention(RUNTIME)
+ @interface ComposedSql {
+
+ @AliasFor(annotation = Sql.class)
+ String[] value() default {};
+
+ @AliasFor(annotation = Sql.class)
+ String[] scripts() default {};
+
+ @AliasFor(annotation = Sql.class)
+ String[] statements() default {};
+
+ @AliasFor(annotation = Sql.class)
+ ExecutionPhase executionPhase();
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationSqlScriptsTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationSqlScriptsTests.java
index 3417c17f..4fc2069e 100644
--- a/spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationSqlScriptsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationSqlScriptsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -31,7 +31,7 @@ import static org.junit.Assert.*;
/**
* Integration tests that verify support for using {@link Sql @Sql} and
- * {@link SqlGroup @SqlGroup} as a meta-annotations.
+ * {@link SqlGroup @SqlGroup} as meta-annotations.
*
* @author Sam Brannen
* @since 4.1
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java
new file mode 100644
index 00000000..f0716780
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/PrimaryDataSourceTests.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2002-2016 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.context.jdbc;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.jdbc.JdbcTestUtils;
+import org.springframework.test.transaction.TransactionTestUtils;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that ensure that <em>primary</em> data sources are
+ * supported.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ * @see org.springframework.test.context.transaction.PrimaryTransactionManagerTests
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@DirtiesContext
+public class PrimaryDataSourceTests {
+
+ @Configuration
+ static class Config {
+
+ @Primary
+ @Bean
+ public DataSource primaryDataSource() {
+ // @formatter:off
+ return new EmbeddedDatabaseBuilder()
+ .generateUniqueName(true)
+ .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")
+ .build();
+ // @formatter:on
+ }
+
+ @Bean
+ public DataSource additionalDataSource() {
+ return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
+ }
+
+ }
+
+
+ private JdbcTemplate jdbcTemplate;
+
+
+ @Autowired
+ public void setDataSource(DataSource dataSource) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ }
+
+ @Test
+ @Sql("data.sql")
+ public void dataSourceTest() {
+ TransactionTestUtils.assertInTransaction(false);
+ assertEquals("Number of rows in the 'user' table.", 1,
+ JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/AbstractTransactionalSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/AbstractTransactionalSpringRunnerTests.java
index 493be806..5760755f 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/AbstractTransactionalSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/AbstractTransactionalSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
* @see MethodLevelTransactionalSpringRunnerTests
* @see Transactional
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration("transactionalTests-context.xml")
public abstract class AbstractTransactionalSpringRunnerTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java
index 23440f7d..4596e6cf 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.Assert.*;
@@ -43,6 +44,7 @@ import static org.springframework.test.transaction.TransactionTestUtils.*;
* @author Sam Brannen
* @since 2.5
*/
+@Transactional
public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactionalSpringRunnerTests {
protected static JdbcTemplate jdbcTemplate;
@@ -79,7 +81,7 @@ public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactio
}
@BeforeTransaction
- public void beforeTransaction() {
+ void beforeTransaction() {
assertInTransaction(false);
this.inTransaction = true;
BeforeAndAfterTransactionAnnotationTests.numBeforeTransactionCalls++;
@@ -88,7 +90,7 @@ public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactio
}
@AfterTransaction
- public void afterTransaction() {
+ void afterTransaction() {
assertInTransaction(false);
this.inTransaction = false;
BeforeAndAfterTransactionAnnotationTests.numAfterTransactionCalls++;
@@ -115,7 +117,6 @@ public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactio
}
@Test
- @Transactional
public void transactionalMethod1() {
assertInTransaction(true);
assertEquals("Adding jane", 1, addPerson(jdbcTemplate, JANE));
@@ -124,7 +125,6 @@ public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactio
}
@Test
- @Transactional
public void transactionalMethod2() {
assertInTransaction(true);
assertEquals("Adding jane", 1, addPerson(jdbcTemplate, JANE));
@@ -134,6 +134,7 @@ public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactio
}
@Test
+ @Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactionalMethod() {
assertInTransaction(false);
assertEquals("Adding luke", 1, addPerson(jdbcTemplate, LUKE));
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelDisabledSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelDisabledSpringRunnerTests.java
index b55abdaa..2db442a9 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelDisabledSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelDisabledSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -30,7 +30,7 @@ import static org.junit.Assert.*;
* @author Juergen Hoeller
* @author Sam Brannen
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@TestExecutionListeners(ClassLevelDisabledSpringRunnerTests.CustomTestExecutionListener.class)
@IfProfileValue(name = "ClassLevelDisabledSpringRunnerTests.profile_value.name", value = "enigmaX")
public class ClassLevelDisabledSpringRunnerTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelTransactionalSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelTransactionalSpringRunnerTests.java
index 8f29a2d0..5eb8c8ed 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelTransactionalSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassLevelTransactionalSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -41,7 +41,7 @@ import static org.springframework.test.transaction.TransactionTestUtils.*;
* {@link Transactional &#64;Transactional}, {@link TestExecutionListeners
* &#64;TestExecutionListeners}, and {@link ContextConfiguration
* &#64;ContextConfiguration} annotations in conjunction with the
- * {@link SpringJUnit4ClassRunner} and the following
+ * {@link SpringRunner} and the following
* {@link TestExecutionListener TestExecutionListeners}:
*
* <ul>
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ContextCustomizerSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ContextCustomizerSpringRunnerTests.java
new file mode 100644
index 00000000..2edfb0d5
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ContextCustomizerSpringRunnerTests.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2002-2016 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.context.junit4;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.BootstrapWith;
+import org.springframework.test.context.ContextCustomizer;
+import org.springframework.test.context.ContextCustomizerFactory;
+import org.springframework.test.context.junit4.ContextCustomizerSpringRunnerTests.CustomTestContextBootstrapper;
+import org.springframework.test.context.support.DefaultTestContextBootstrapper;
+
+import static java.util.Collections.*;
+import static org.junit.Assert.*;
+
+/**
+ * JUnit 4 based integration test which verifies support of
+ * {@link ContextCustomizerFactory} and {@link ContextCustomizer}.
+ *
+ * @author Sam Brannen
+ * @author Phillip Webb
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+@BootstrapWith(CustomTestContextBootstrapper.class)
+public class ContextCustomizerSpringRunnerTests {
+
+ @Autowired String foo;
+
+
+ @Test
+ public void injectedBean() {
+ assertEquals("foo", foo);
+ }
+
+
+ static class CustomTestContextBootstrapper extends DefaultTestContextBootstrapper {
+
+ @Override
+ protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
+ return singletonList(
+ (ContextCustomizerFactory) (testClass, configAttributes) ->
+ (ContextCustomizer) (context, mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "foo")
+ );
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java
index aab80b71..be8a2ff9 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -37,7 +37,7 @@ import static org.junit.Assert.*;
* @author Sam Brannen
* @since 3.0
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@BootstrapWith(CustomDefaultContextLoaderClassSpringRunnerTests.PropertiesBasedTestContextBootstrapper.class)
@ContextConfiguration("PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties")
public class CustomDefaultContextLoaderClassSpringRunnerTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackFalseRollbackAnnotationTransactionalTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackFalseRollbackAnnotationTransactionalTests.java
index 0c1f043a..1b027899 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackFalseRollbackAnnotationTransactionalTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackFalseRollbackAnnotationTransactionalTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -45,7 +45,7 @@ import static org.springframework.test.transaction.TransactionTestUtils.*;
* @see Transactional#transactionManager
* @see DefaultRollbackFalseTransactionalTests
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration(classes = EmbeddedPersonDatabaseTestsConfig.class, inheritLocations = false)
@Transactional("txMgr")
@Rollback(false)
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackTrueRollbackAnnotationTransactionalTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackTrueRollbackAnnotationTransactionalTests.java
index 984fc62f..49f70f6d 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackTrueRollbackAnnotationTransactionalTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/DefaultRollbackTrueRollbackAnnotationTransactionalTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -45,7 +45,7 @@ import static org.springframework.test.transaction.TransactionTestUtils.*;
* @see Transactional#transactionManager
* @see DefaultRollbackTrueTransactionalTests
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration(classes = EmbeddedPersonDatabaseTestsConfig.class, inheritLocations = false)
@Transactional("txMgr")
@Rollback(true)
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/EnabledAndIgnoredSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/EnabledAndIgnoredSpringRunnerTests.java
index a445fcaf..f4e84996 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/EnabledAndIgnoredSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/EnabledAndIgnoredSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -34,7 +34,7 @@ import static org.junit.Assert.*;
* {@link IfProfileValue &#064;IfProfileValue} and
* {@link ProfileValueSourceConfiguration &#064;ProfileValueSourceConfiguration}
* (with the <em>implicit, default {@link ProfileValueSource}</em>) annotations in
- * conjunction with the {@link SpringJUnit4ClassRunner}.
+ * conjunction with the {@link SpringRunner}.
* <p>
* Note that {@link TestExecutionListeners &#064;TestExecutionListeners} is
* explicitly configured with an empty list, thus disabling all default
@@ -44,7 +44,7 @@ import static org.junit.Assert.*;
* @since 2.5
* @see HardCodedProfileValueSourceSpringRunnerTests
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@TestExecutionListeners( {})
public class EnabledAndIgnoredSpringRunnerTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java
index 5fc3947d..4cfb4699 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -29,7 +29,7 @@ import static org.springframework.test.context.junit4.JUnitTestingUtils.*;
/**
* Verifies proper handling of JUnit's {@link Test#expected() &#064;Test(expected=...)}
- * support in conjunction with the {@link SpringJUnit4ClassRunner}.
+ * support in conjunction with the {@link SpringRunner}.
*
* @author Sam Brannen
* @since 3.0
@@ -39,7 +39,7 @@ public class ExpectedExceptionSpringRunnerTests {
@Test
public void expectedExceptions() throws Exception {
- runTestsAndAssertCounters(SpringJUnit4ClassRunner.class, ExpectedExceptionSpringRunnerTestCase.class, 1, 0, 1, 0, 0);
+ runTestsAndAssertCounters(SpringRunner.class, ExpectedExceptionSpringRunnerTestCase.class, 1, 0, 1, 0, 0);
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java
index daebe2bc..4918606f 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -79,7 +79,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
protected Class<? extends Runner> getRunnerClass() {
- return SpringJUnit4ClassRunner.class;
+ return SpringRunner.class;
}
@Test
@@ -133,7 +133,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
}
- @RunWith(SpringJUnit4ClassRunner.class)
+ @RunWith(SpringRunner.class)
@TestExecutionListeners({})
public static abstract class BaseTestCase {
@@ -168,7 +168,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
@Ignore("TestCase classes are run manually by the enclosing test class")
- @RunWith(SpringJUnit4ClassRunner.class)
+ @RunWith(SpringRunner.class)
@ContextConfiguration("FailingBeforeAndAfterMethodsTests-context.xml")
@Transactional
public static class FailingBeforeTransactionTestCase {
@@ -184,7 +184,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
@Ignore("TestCase classes are run manually by the enclosing test class")
- @RunWith(SpringJUnit4ClassRunner.class)
+ @RunWith(SpringRunner.class)
@ContextConfiguration("FailingBeforeAndAfterMethodsTests-context.xml")
@Transactional
public static class FailingAfterTransactionTestCase {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/HardCodedProfileValueSourceSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/HardCodedProfileValueSourceSpringRunnerTests.java
index 9a26751f..b0e18843 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/HardCodedProfileValueSourceSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/HardCodedProfileValueSourceSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -28,7 +28,7 @@ import org.springframework.test.annotation.ProfileValueSourceConfiguration;
* &#064;IfProfileValue} and {@link ProfileValueSourceConfiguration
* &#064;ProfileValueSourceConfiguration} (with an
* <em>explicit, custom defined {@link ProfileValueSource}</em>) annotations in
- * conjunction with the {@link SpringJUnit4ClassRunner}.
+ * conjunction with the {@link SpringRunner}.
* </p>
*
* @author Sam Brannen
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java
index 42700595..f9b6b749 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -40,7 +40,7 @@ import static org.springframework.test.transaction.TransactionTestUtils.*;
* {@link Transactional &#64;Transactional}, {@link TestExecutionListeners
* &#64;TestExecutionListeners}, and {@link ContextConfiguration
* &#64;ContextConfiguration} annotations in conjunction with the
- * {@link SpringJUnit4ClassRunner} and the following
+ * {@link SpringRunner} and the following
* {@link TestExecutionListener TestExecutionListeners}:
*
* <ul>
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/OptionalContextConfigurationSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/OptionalContextConfigurationSpringRunnerTests.java
new file mode 100644
index 00000000..5cb4ce83
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/OptionalContextConfigurationSpringRunnerTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2016 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.context.junit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * JUnit 4 based integration test which verifies that {@link @ContextConfiguration}
+ * is optional.
+ *
+ * @author Phillip Webb
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+public class OptionalContextConfigurationSpringRunnerTests {
+
+ @Autowired
+ String foo;
+
+
+ @Test
+ public void contextConfigurationAnnotationIsOptional() {
+ assertEquals("foo", foo);
+ }
+
+
+ @Configuration
+ static class Config {
+
+ @Bean
+ String foo() {
+ return "foo";
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests.java
index acbb6cc2..b52a9fa1 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -32,7 +32,7 @@ import static org.junit.Assert.*;
/**
* <p>
* JUnit 4 based test class, which verifies the expected functionality of
- * {@link SpringJUnit4ClassRunner} in conjunction with support for application contexts
+ * {@link SpringRunner} in conjunction with support for application contexts
* loaded from Java {@link Properties} files. Specifically, the
* {@link ContextConfiguration#loader() loader} attribute of {@code ContextConfiguration}
* and the
@@ -54,7 +54,7 @@ import static org.junit.Assert.*;
* @see GenericPropertiesContextLoader
* @see SpringJUnit4ClassRunnerAppCtxTests
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration(loader = GenericPropertiesContextLoader.class)
public class PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java
index 94a884bf..d2bf46bf 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -38,7 +38,7 @@ import static org.springframework.test.context.junit4.JUnitTestingUtils.*;
/**
* Verifies proper handling of the following in conjunction with the
- * {@link SpringJUnit4ClassRunner}:
+ * {@link SpringRunner}:
* <ul>
* <li>Spring's {@link Repeat @Repeat}</li>
* <li>Spring's {@link Timed @Timed}</li>
@@ -82,7 +82,7 @@ public class RepeatedSpringRunnerTests {
}
protected Class<? extends Runner> getRunnerClass() {
- return SpringJUnit4ClassRunner.class;
+ return SpringRunner.class;
}
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit47ClassRunnerRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit47ClassRunnerRuleTests.java
index 6f0eddff..e59cdde9 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit47ClassRunnerRuleTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit47ClassRunnerRuleTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -27,14 +27,14 @@ import static org.junit.Assert.*;
/**
* Verifies support for JUnit 4.7 {@link Rule Rules} in conjunction with the
- * {@link SpringJUnit4ClassRunner}. The body of this test class is taken from
- * the JUnit 4.7 release notes.
+ * {@link SpringRunner}. The body of this test class is taken from the
+ * JUnit 4.7 release notes.
*
* @author JUnit 4.7 Team
* @author Sam Brannen
* @since 3.0
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@TestExecutionListeners( {})
public class SpringJUnit47ClassRunnerRuleTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests.java
index 292aebfc..c60745d3 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -43,7 +43,7 @@ import static org.junit.Assert.*;
/**
* SpringJUnit4ClassRunnerAppCtxTests serves as a <em>proof of concept</em>
* JUnit 4 based test class, which verifies the expected functionality of
- * {@link SpringJUnit4ClassRunner} in conjunction with the following:
+ * {@link SpringRunner} in conjunction with the following:
*
* <ul>
* <li>{@link ContextConfiguration @ContextConfiguration}</li>
@@ -73,7 +73,7 @@ import static org.junit.Assert.*;
* @see RelativePathSpringJUnit4ClassRunnerAppCtxTests
* @see InheritedConfigSpringJUnit4ClassRunnerAppCtxTests
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration
@TestExecutionListeners(DependencyInjectionTestExecutionListener.class)
public class SpringJUnit4ClassRunnerAppCtxTests implements ApplicationContextAware, BeanNameAware, InitializingBean {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java
index 8f301117..020bda60 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -45,12 +45,12 @@ import org.springframework.test.context.junit4.profile.xml.DevProfileXmlConfigTe
import org.springframework.test.context.transaction.programmatic.ProgrammaticTxMgmtTests;
/**
- * JUnit test suite for tests involving {@link SpringJUnit4ClassRunner} and the
+ * JUnit test suite for tests involving {@link SpringRunner} and the
* <em>Spring TestContext Framework</em>; only intended to be run manually as a
* convenience.
*
* <p>This test suite serves a dual purpose of verifying that tests run with
- * {@link SpringJUnit4ClassRunner} can be used in conjunction with JUnit's
+ * {@link SpringRunner} can be used in conjunction with JUnit's
* {@link Suite} runner.
*
* <p>Note that tests included in this suite will be executed at least twice if
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/StandardJUnit4FeaturesSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/StandardJUnit4FeaturesSpringRunnerTests.java
index 4dd270ca..f4cda4ea 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/StandardJUnit4FeaturesSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/StandardJUnit4FeaturesSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -22,7 +22,7 @@ import org.springframework.test.context.TestExecutionListeners;
/**
* <p>
- * Simple unit test to verify that {@link SpringJUnit4ClassRunner} does not
+ * Simple unit test to verify that {@link SpringRunner} does not
* hinder correct functionality of standard JUnit 4.4+ testing features.
* </p>
* <p>
@@ -35,7 +35,7 @@ import org.springframework.test.context.TestExecutionListeners;
* @since 2.5
* @see StandardJUnit4FeaturesTests
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class StandardJUnit4FeaturesSpringRunnerTests extends StandardJUnit4FeaturesTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java
index d59bc140..cede912c 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -32,7 +32,7 @@ import static org.springframework.test.context.junit4.JUnitTestingUtils.*;
/**
* Verifies proper handling of the following in conjunction with the
- * {@link SpringJUnit4ClassRunner}:
+ * {@link SpringRunner}:
* <ul>
* <li>JUnit's {@link Test#timeout() @Test(timeout=...)}</li>
* <li>Spring's {@link Timed @Timed}</li>
@@ -49,7 +49,7 @@ public class TimedSpringRunnerTests {
}
protected Class<? extends Runner> getRunnerClass() {
- return SpringJUnit4ClassRunner.class;
+ return SpringRunner.class;
}
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedTransactionalSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedTransactionalSpringRunnerTests.java
index 3fcdb040..cffe292f 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedTransactionalSpringRunnerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedTransactionalSpringRunnerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -36,7 +36,7 @@ import static org.springframework.test.transaction.TransactionTestUtils.*;
* @author Sam Brannen
* @since 2.5
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration("transactionalTests-context.xml")
@Transactional
public class TimedTransactionalSpringRunnerTests {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/BarConfig.java b/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/BarConfig.java
new file mode 100644
index 00000000..1ee6b6fd
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/BarConfig.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2016 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.context.junit4.aci.annotation;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@Configuration
+class BarConfig {
+
+ @Bean
+ String bar() {
+ return "bar";
+ }
+
+} \ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/FooConfig.java b/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/FooConfig.java
new file mode 100644
index 00000000..a080fd84
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/FooConfig.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2016 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.context.junit4.aci.annotation;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@Configuration
+class FooConfig {
+
+ @Bean
+ String foo() {
+ return "foo";
+ }
+
+} \ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/InitializerConfiguredViaMetaAnnotationTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/InitializerConfiguredViaMetaAnnotationTests.java
new file mode 100644
index 00000000..971e3f4b
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/aci/annotation/InitializerConfiguredViaMetaAnnotationTests.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2002-2016 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.context.junit4.aci.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.junit4.aci.annotation.InitializerConfiguredViaMetaAnnotationTests.ComposedContextConfiguration;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Integration test that demonstrates how to register one or more {@code @Configuration}
+ * classes via an {@link ApplicationContextInitializer} in a composed annotation so
+ * that certain {@code @Configuration} classes are always registered whenever the composed
+ * annotation is used, even if the composed annotation is used to declare additional
+ * {@code @Configuration} classes.
+ *
+ * <p>This class has been implemented in response to the following Stack Overflow question:
+ * <a href="http://stackoverflow.com/questions/35733344/can-contextconfiguration-in-a-custom-annotation-be-merged">
+ * Can {@code @ContextConfiguration} in a custom annotation be merged?</a>
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+@ComposedContextConfiguration(BarConfig.class)
+public class InitializerConfiguredViaMetaAnnotationTests {
+
+ @Autowired
+ String foo;
+
+ @Autowired
+ String bar;
+
+ @Autowired
+ List<String> strings;
+
+
+ @Test
+ public void beansFromInitializerAndComposedAnnotation() {
+ assertEquals(2, strings.size());
+ assertEquals("foo", foo);
+ assertEquals("bar", bar);
+ }
+
+
+ static class FooConfigInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
+
+ @Override
+ public void initialize(GenericApplicationContext applicationContext) {
+ new AnnotatedBeanDefinitionReader(applicationContext).register(FooConfig.class);
+ }
+ }
+
+ @ContextConfiguration(loader = AnnotationConfigContextLoader.class, initializers = FooConfigInitializer.class)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ @interface ComposedContextConfiguration {
+
+ @AliasFor(annotation = ContextConfiguration.class, attribute = "classes")
+ Class<?>[] value() default {};
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsContextInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsContextInitializerTests.java
index 4b70a8cb..f92c7418 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsContextInitializerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsContextInitializerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,6 +16,7 @@
package org.springframework.test.context.support;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -35,63 +36,62 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
* @author Sam Brannen
* @since 3.1
*/
+@SuppressWarnings("unchecked")
public class BootstrapTestUtilsContextInitializerTests extends AbstractContextConfigurationUtilsTests {
@Test
- public void buildMergedConfigWithLocalInitializer() {
- Class<?> testClass = InitializersFoo.class;
- Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class };
- Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
- = new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
- expectedInitializerClasses.add(FooInitializer.class);
+ public void buildMergedConfigWithSingleLocalInitializer() {
+ Class<?> testClass = SingleInitializer.class;
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
+
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY,
+ initializers(FooInitializer.class), DelegatingSmartContextLoader.class);
+ }
+ @Test
+ public void buildMergedConfigWithLocalInitializerAndConfigClass() {
+ Class<?> testClass = InitializersFoo.class;
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
- assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
- DelegatingSmartContextLoader.class);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, classes(FooConfig.class),
+ initializers(FooInitializer.class), DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedConfigWithLocalAndInheritedInitializer() {
Class<?> testClass = InitializersBar.class;
- Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
- Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
- = new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
- expectedInitializerClasses.add(FooInitializer.class);
- expectedInitializerClasses.add(BarInitializer.class);
-
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
- assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
- DelegatingSmartContextLoader.class);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, classes(FooConfig.class, BarConfig.class),
+ initializers(FooInitializer.class, BarInitializer.class), DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedConfigWithOverriddenInitializers() {
Class<?> testClass = OverriddenInitializersBar.class;
- Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
- Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
- = new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
- expectedInitializerClasses.add(BarInitializer.class);
-
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
- assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
- DelegatingSmartContextLoader.class);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, classes(FooConfig.class, BarConfig.class),
+ initializers(BarInitializer.class), DelegatingSmartContextLoader.class);
}
@Test
public void buildMergedConfigWithOverriddenInitializersAndClasses() {
Class<?> testClass = OverriddenInitializersAndClassesBar.class;
- Class<?>[] expectedClasses = new Class<?>[] { BarConfig.class };
- Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> expectedInitializerClasses//
- = new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
- expectedInitializerClasses.add(BarInitializer.class);
-
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
- assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
- DelegatingSmartContextLoader.class);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, classes(BarConfig.class),
+ initializers(BarInitializer.class), DelegatingSmartContextLoader.class);
+ }
+
+ private Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializers(
+ Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>... classes) {
+
+ return new HashSet<>(Arrays.asList(classes));
+ }
+
+ private Class<?>[] classes(Class<?>... classes) {
+ return classes;
}
@@ -109,6 +109,10 @@ public class BootstrapTestUtilsContextInitializerTests extends AbstractContextCo
}
}
+ @ContextConfiguration(initializers = FooInitializer.class)
+ private static class SingleInitializer {
+ }
+
@ContextConfiguration(classes = FooConfig.class, initializers = FooInitializer.class)
private static class InitializersFoo {
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java
index cef782e2..b499c257 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -21,15 +21,20 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.springframework.test.context.BootstrapTestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.web.WebDelegatingSmartContextLoader;
import org.springframework.test.context.web.WebMergedContextConfiguration;
-import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
/**
* Unit tests for {@link BootstrapTestUtils} involving {@link MergedContextConfiguration}.
@@ -39,12 +44,28 @@ import static org.junit.Assert.*;
*/
public class BootstrapTestUtilsMergedConfigTests extends AbstractContextConfigurationUtilsTests {
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+
@Test
- public void buildMergedConfigWithoutAnnotation() {
+ public void buildImplicitMergedConfigWithoutAnnotation() {
Class<?> testClass = Enigma.class;
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
- assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, null);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, DelegatingSmartContextLoader.class);
+ }
+
+ /**
+ * @since 4.3
+ */
+ @Test
+ public void buildMergedConfigWithContextConfigurationWithoutLocationsClassesOrInitializers() {
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage(startsWith("DelegatingSmartContextLoader was unable to detect defaults, "
+ + "and no ApplicationContextInitializers or ContextCustomizers were declared for context configuration attributes"));
+
+ buildMergedContextConfiguration(MissingContextAttributesTestCase.class);
}
@Test
@@ -200,4 +221,8 @@ public class BootstrapTestUtilsMergedConfigTests extends AbstractContextConfigur
public static class GermanShepherd extends WorkingDog {
}
+ @ContextConfiguration
+ static class MissingContextAttributesTestCase {
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java
index 3faab204..37025fdf 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsContextHierarchyTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -59,9 +59,13 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextConf
resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchyOnSingleMetaAnnotation.class);
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() {
- resolveContextHierarchyAttributes(BareAnnotations.class);
+ List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(BareAnnotations.class);
+ assertEquals(1, hierarchyAttributes.size());
+ List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0);
+ assertEquals(1, configAttributesList.size());
+ debugConfigAttributes(configAttributesList);
}
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java b/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java
index 0395ee36..8f416cfa 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/DelegatingSmartContextLoaderTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -56,17 +56,6 @@ public class DelegatingSmartContextLoaderTests {
// --- SmartContextLoader - processContextConfiguration() ------------------
@Test
- public void processContextConfigurationWithoutLocationsAndConfigurationClassesForBogusTestClass() {
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage(startsWith("Neither"));
- expectedException.expectMessage(containsString("was able to detect defaults"));
-
- ContextConfigurationAttributes configAttributes = new ContextConfigurationAttributes(getClass(),
- EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, true, null, true, ContextLoader.class);
- loader.processContextConfiguration(configAttributes);
- }
-
- @Test
public void processContextConfigurationWithDefaultXmlConfigGeneration() {
ContextConfigurationAttributes configAttributes = new ContextConfigurationAttributes(XmlTestCase.class,
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, true, null, true, ContextLoader.class);
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java
index 8f6c8d7e..b1d7ddfd 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -26,13 +26,17 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.ResourceLoader;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.test.context.TestPropertySource;
import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.*;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.*;
import static org.springframework.test.context.support.TestPropertySourceUtils.*;
/**
@@ -44,20 +48,16 @@ import static org.springframework.test.context.support.TestPropertySourceUtils.*
public class TestPropertySourceUtilsTests {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final String[] KEY_VALUE_PAIR = new String[] { "key = value" };
+
+ private static final String[] KEY_VALUE_PAIR = new String[] {"key = value"};
+
+ private static final String[] FOO_LOCATIONS = new String[] {"classpath:/foo.properties"};
+
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private void assertMergedTestPropertySources(Class<?> testClass, String[] expectedLocations,
- String[] expectedProperties) {
- MergedTestPropertySources mergedPropertySources = buildMergedTestPropertySources(testClass);
- assertNotNull(mergedPropertySources);
- assertArrayEquals(expectedLocations, mergedPropertySources.getLocations());
- assertArrayEquals(expectedProperties, mergedPropertySources.getProperties());
- }
-
@Test
public void emptyAnnotation() {
expectedException.expect(IllegalStateException.class);
@@ -76,8 +76,8 @@ public class TestPropertySourceUtilsTests {
@Test
public void value() {
- assertMergedTestPropertySources(ValuePropertySources.class, new String[] { "classpath:/value.xml" },
- EMPTY_STRING_ARRAY);
+ assertMergedTestPropertySources(ValuePropertySources.class, asArray("classpath:/value.xml"),
+ EMPTY_STRING_ARRAY);
}
@Test
@@ -88,44 +88,88 @@ public class TestPropertySourceUtilsTests {
@Test
public void locationsAndProperties() {
- assertMergedTestPropertySources(LocationsAndPropertiesPropertySources.class, new String[] {
- "classpath:/foo1.xml", "classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
+ assertMergedTestPropertySources(LocationsAndPropertiesPropertySources.class,
+ asArray("classpath:/foo1.xml", "classpath:/foo2.xml"), asArray("k1a=v1a", "k1b: v1b"));
}
@Test
public void inheritedLocationsAndProperties() {
- assertMergedTestPropertySources(InheritedPropertySources.class, new String[] { "classpath:/foo1.xml",
- "classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
+ assertMergedTestPropertySources(InheritedPropertySources.class,
+ asArray("classpath:/foo1.xml", "classpath:/foo2.xml"), asArray("k1a=v1a", "k1b: v1b"));
}
@Test
public void extendedLocationsAndProperties() {
- assertMergedTestPropertySources(ExtendedPropertySources.class, new String[] { "classpath:/foo1.xml",
- "classpath:/foo2.xml", "classpath:/bar1.xml", "classpath:/bar2.xml" }, new String[] { "k1a=v1a",
- "k1b: v1b", "k2a v2a", "k2b: v2b" });
+ assertMergedTestPropertySources(ExtendedPropertySources.class,
+ asArray("classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/bar1.xml", "classpath:/bar2.xml"),
+ asArray("k1a=v1a", "k1b: v1b", "k2a v2a", "k2b: v2b"));
}
@Test
public void overriddenLocations() {
assertMergedTestPropertySources(OverriddenLocationsPropertySources.class,
- new String[] { "classpath:/baz.properties" }, new String[] { "k1a=v1a", "k1b: v1b", "key = value" });
+ asArray("classpath:/baz.properties"), asArray("k1a=v1a", "k1b: v1b", "key = value"));
}
@Test
public void overriddenProperties() {
- assertMergedTestPropertySources(OverriddenPropertiesPropertySources.class, new String[] {
- "classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties" }, KEY_VALUE_PAIR);
+ assertMergedTestPropertySources(OverriddenPropertiesPropertySources.class,
+ asArray("classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties"), KEY_VALUE_PAIR);
}
@Test
public void overriddenLocationsAndProperties() {
assertMergedTestPropertySources(OverriddenLocationsAndPropertiesPropertySources.class,
- new String[] { "classpath:/baz.properties" }, KEY_VALUE_PAIR);
+ asArray("classpath:/baz.properties"), KEY_VALUE_PAIR);
+ }
+
+
+ @Test
+ public void addPropertiesFilesToEnvironmentWithNullContext() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("must not be null");
+ addPropertiesFilesToEnvironment((ConfigurableApplicationContext) null, FOO_LOCATIONS);
+ }
+
+ @Test
+ public void addPropertiesFilesToEnvironmentWithContextAndNullLocations() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("must not be null");
+ addPropertiesFilesToEnvironment(mock(ConfigurableApplicationContext.class), (String[]) null);
+ }
+
+ @Test
+ public void addPropertiesFilesToEnvironmentWithNullEnvironment() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("must not be null");
+ addPropertiesFilesToEnvironment((ConfigurableEnvironment) null, mock(ResourceLoader.class), FOO_LOCATIONS);
+ }
+
+ @Test
+ public void addPropertiesFilesToEnvironmentWithEnvironmentAndNullLocations() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("must not be null");
+ addPropertiesFilesToEnvironment(new MockEnvironment(), mock(ResourceLoader.class), (String[]) null);
+ }
+
+ @Test
+ public void addPropertiesFilesToEnvironmentWithSinglePropertyFromVirtualFile() {
+ ConfigurableEnvironment environment = new MockEnvironment();
+
+ MutablePropertySources propertySources = environment.getPropertySources();
+ propertySources.remove(MockPropertySource.MOCK_PROPERTIES_PROPERTY_SOURCE_NAME);
+ assertEquals(0, propertySources.size());
+
+ String pair = "key = value";
+ ByteArrayResource resource = new ByteArrayResource(pair.getBytes(), "from inlined property: " + pair);
+ ResourceLoader resourceLoader = mock(ResourceLoader.class);
+ when(resourceLoader.getResource(anyString())).thenReturn(resource);
+
+ addPropertiesFilesToEnvironment(environment, resourceLoader, FOO_LOCATIONS);
+ assertEquals(1, propertySources.size());
+ assertEquals("value", environment.getProperty("key"));
}
- /**
- * @since 4.1.5
- */
@Test
public void addInlinedPropertiesToEnvironmentWithNullContext() {
expectedException.expect(IllegalArgumentException.class);
@@ -133,19 +177,13 @@ public class TestPropertySourceUtilsTests {
addInlinedPropertiesToEnvironment((ConfigurableApplicationContext) null, KEY_VALUE_PAIR);
}
- /**
- * @since 4.1.5
- */
@Test
public void addInlinedPropertiesToEnvironmentWithContextAndNullInlinedProperties() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("inlined");
- addInlinedPropertiesToEnvironment(mock(ConfigurableApplicationContext.class), null);
+ addInlinedPropertiesToEnvironment(mock(ConfigurableApplicationContext.class), (String[]) null);
}
- /**
- * @since 4.1.5
- */
@Test
public void addInlinedPropertiesToEnvironmentWithNullEnvironment() {
expectedException.expect(IllegalArgumentException.class);
@@ -153,39 +191,27 @@ public class TestPropertySourceUtilsTests {
addInlinedPropertiesToEnvironment((ConfigurableEnvironment) null, KEY_VALUE_PAIR);
}
- /**
- * @since 4.1.5
- */
@Test
public void addInlinedPropertiesToEnvironmentWithEnvironmentAndNullInlinedProperties() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("inlined");
- addInlinedPropertiesToEnvironment(new MockEnvironment(), null);
+ addInlinedPropertiesToEnvironment(new MockEnvironment(), (String[]) null);
}
- /**
- * @since 4.1.5
- */
@Test
public void addInlinedPropertiesToEnvironmentWithMalformedUnicodeInValue() {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Failed to load test environment property");
- addInlinedPropertiesToEnvironment(new MockEnvironment(), new String[] { "key = \\uZZZZ" });
+ addInlinedPropertiesToEnvironment(new MockEnvironment(), asArray("key = \\uZZZZ"));
}
- /**
- * @since 4.1.5
- */
@Test
public void addInlinedPropertiesToEnvironmentWithMultipleKeyValuePairsInSingleInlinedProperty() {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Failed to load exactly one test environment property");
- addInlinedPropertiesToEnvironment(new MockEnvironment(), new String[] { "a=b\nx=y" });
+ addInlinedPropertiesToEnvironment(new MockEnvironment(), asArray("a=b\nx=y"));
}
- /**
- * @since 4.1.5
- */
@Test
@SuppressWarnings("rawtypes")
public void addInlinedPropertiesToEnvironmentWithEmptyProperty() {
@@ -193,7 +219,7 @@ public class TestPropertySourceUtilsTests {
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.remove(MockPropertySource.MOCK_PROPERTIES_PROPERTY_SOURCE_NAME);
assertEquals(0, propertySources.size());
- addInlinedPropertiesToEnvironment(environment, new String[] { " " });
+ addInlinedPropertiesToEnvironment(environment, asArray(" "));
assertEquals(1, propertySources.size());
assertEquals(0, ((Map) propertySources.iterator().next().getSource()).size());
}
@@ -202,10 +228,25 @@ public class TestPropertySourceUtilsTests {
public void convertInlinedPropertiesToMapWithNullInlinedProperties() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("inlined");
- convertInlinedPropertiesToMap(null);
+ convertInlinedPropertiesToMap((String[]) null);
+ }
+
+
+ private static void assertMergedTestPropertySources(Class<?> testClass, String[] expectedLocations,
+ String[] expectedProperties) {
+
+ MergedTestPropertySources mergedPropertySources = buildMergedTestPropertySources(testClass);
+ assertNotNull(mergedPropertySources);
+ assertArrayEquals(expectedLocations, mergedPropertySources.getLocations());
+ assertArrayEquals(expectedProperties, mergedPropertySources.getProperties());
+ }
+
+
+ @SafeVarargs
+ private static <T> T[] asArray(T... arr) {
+ return arr;
}
- // -------------------------------------------------------------------
@TestPropertySource
static class EmptyPropertySources {
diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/AnnotationConfigTransactionalTestNGSpringContextTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/AnnotationConfigTransactionalTestNGSpringContextTests.java
index a7c9f974..6c515027 100644
--- a/spring-test/src/test/java/org/springframework/test/context/testng/AnnotationConfigTransactionalTestNGSpringContextTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/testng/AnnotationConfigTransactionalTestNGSpringContextTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -54,8 +54,8 @@ import static org.testng.Assert.*;
* @since 3.1
*/
@ContextConfiguration
-public class AnnotationConfigTransactionalTestNGSpringContextTests extends
- AbstractTransactionalTestNGSpringContextTests {
+public class AnnotationConfigTransactionalTestNGSpringContextTests
+ extends AbstractTransactionalTestNGSpringContextTests {
private static final String JANE = "jane";
private static final String SUE = "sue";
@@ -94,7 +94,7 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
}
@BeforeClass
- public void beforeClass() {
+ void beforeClass() {
numSetUpCalls = 0;
numSetUpCallsInTransaction = 0;
numTearDownCalls = 0;
@@ -102,7 +102,7 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
}
@AfterClass
- public void afterClass() {
+ void afterClass() {
assertEquals(numSetUpCalls, NUM_TESTS, "number of calls to setUp().");
assertEquals(numSetUpCallsInTransaction, NUM_TX_TESTS, "number of calls to setUp() within a transaction.");
assertEquals(numTearDownCalls, NUM_TESTS, "number of calls to tearDown().");
@@ -111,7 +111,7 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void autowiringFromConfigClass() {
+ void autowiringFromConfigClass() {
assertNotNull(employee, "The employee should have been autowired.");
assertEquals(employee.getName(), "John Smith");
@@ -120,13 +120,13 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
}
@BeforeTransaction
- public void beforeTransaction() {
+ void beforeTransaction() {
assertNumRowsInPersonTable(1, "before a transactional test method");
assertAddPerson(YODA);
}
@BeforeMethod
- public void setUp() throws Exception {
+ void setUp() throws Exception {
numSetUpCalls++;
if (inTransaction()) {
numSetUpCallsInTransaction++;
@@ -135,7 +135,7 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
}
@Test
- public void modifyTestDataWithinTransaction() {
+ void modifyTestDataWithinTransaction() {
assertInTransaction(true);
assertAddPerson(JANE);
assertAddPerson(SUE);
@@ -143,7 +143,7 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
}
@AfterMethod
- public void tearDown() throws Exception {
+ void tearDown() throws Exception {
numTearDownCalls++;
if (inTransaction()) {
numTearDownCallsInTransaction++;
@@ -152,7 +152,7 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
}
@AfterTransaction
- public void afterTransaction() {
+ void afterTransaction() {
assertEquals(deletePerson(YODA), 1, "Deleting yoda");
assertNumRowsInPersonTable(1, "after a transactional test method");
}
@@ -162,7 +162,7 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
static class ContextConfiguration {
@Bean
- public Employee employee() {
+ Employee employee() {
Employee employee = new Employee();
employee.setName("John Smith");
employee.setAge(42);
@@ -171,17 +171,17 @@ public class AnnotationConfigTransactionalTestNGSpringContextTests extends
}
@Bean
- public Pet pet() {
+ Pet pet() {
return new Pet("Fido");
}
@Bean
- public PlatformTransactionManager transactionManager() {
+ PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
- public DataSource dataSource() {
+ DataSource dataSource() {
return new EmbeddedDatabaseBuilder()//
.addScript("classpath:/org/springframework/test/jdbc/schema.sql")//
.addScript("classpath:/org/springframework/test/jdbc/data.sql")//
diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/ConcreteTransactionalTestNGSpringContextTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/ConcreteTransactionalTestNGSpringContextTests.java
index 2acc370e..d249fc38 100644
--- a/spring-test/src/test/java/org/springframework/test/context/testng/ConcreteTransactionalTestNGSpringContextTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/testng/ConcreteTransactionalTestNGSpringContextTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -117,7 +117,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
}
@BeforeClass
- public void beforeClass() {
+ void beforeClass() {
numSetUpCalls = 0;
numSetUpCallsInTransaction = 0;
numTearDownCalls = 0;
@@ -125,7 +125,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
}
@AfterClass
- public void afterClass() {
+ void afterClass() {
assertEquals(numSetUpCalls, NUM_TESTS, "number of calls to setUp().");
assertEquals(numSetUpCallsInTransaction, NUM_TX_TESTS, "number of calls to setUp() within a transaction.");
assertEquals(numTearDownCalls, NUM_TESTS, "number of calls to tearDown().");
@@ -134,7 +134,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void verifyApplicationContextSet() {
+ void verifyApplicationContextSet() {
assertInTransaction(false);
assertNotNull(super.applicationContext,
"The application context should have been set due to ApplicationContextAware semantics.");
@@ -144,7 +144,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void verifyBeanInitialized() {
+ void verifyBeanInitialized() {
assertInTransaction(false);
assertTrue(beanInitialized,
"This test instance should have been initialized due to InitializingBean semantics.");
@@ -152,7 +152,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void verifyBeanNameSet() {
+ void verifyBeanNameSet() {
assertInTransaction(false);
assertEquals(beanName, getClass().getName(),
"The bean name of this test instance should have been set due to BeanNameAware semantics.");
@@ -160,7 +160,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void verifyAnnotationAutowiredFields() {
+ void verifyAnnotationAutowiredFields() {
assertInTransaction(false);
assertNull(nonrequiredLong, "The nonrequiredLong field should NOT have been autowired.");
assertNotNull(pet, "The pet field should have been autowired.");
@@ -169,7 +169,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void verifyAnnotationAutowiredMethods() {
+ void verifyAnnotationAutowiredMethods() {
assertInTransaction(false);
assertNotNull(employee, "The setEmployee() method should have been autowired.");
assertEquals(employee.getName(), "John Smith", "employee's name.");
@@ -177,26 +177,26 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void verifyResourceAnnotationInjectedFields() {
+ void verifyResourceAnnotationInjectedFields() {
assertInTransaction(false);
assertEquals(foo, "Foo", "The foo field should have been injected via @Resource.");
}
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
- public void verifyResourceAnnotationInjectedMethods() {
+ void verifyResourceAnnotationInjectedMethods() {
assertInTransaction(false);
assertEquals(bar, "Bar", "The setBar() method should have been injected via @Resource.");
}
@BeforeTransaction
- public void beforeTransaction() {
+ void beforeTransaction() {
assertNumRowsInPersonTable(1, "before a transactional test method");
assertAddPerson(YODA);
}
@BeforeMethod
- public void setUp() throws Exception {
+ void setUp() throws Exception {
numSetUpCalls++;
if (inTransaction()) {
numSetUpCallsInTransaction++;
@@ -205,7 +205,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
}
@Test
- public void modifyTestDataWithinTransaction() {
+ void modifyTestDataWithinTransaction() {
assertInTransaction(true);
assertAddPerson(JANE);
assertAddPerson(SUE);
@@ -213,7 +213,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
}
@AfterMethod
- public void tearDown() throws Exception {
+ void tearDown() throws Exception {
numTearDownCalls++;
if (inTransaction()) {
numTearDownCallsInTransaction++;
@@ -222,7 +222,7 @@ public class ConcreteTransactionalTestNGSpringContextTests extends AbstractTrans
}
@AfterTransaction
- public void afterTransaction() {
+ void afterTransaction() {
assertEquals(deletePerson(YODA), 1, "Deleting yoda");
assertNumRowsInPersonTable(1, "after a transactional test method");
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java
index 25f33425..6de10205 100644
--- a/spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/testng/web/ServletTestExecutionListenerTestNGIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -57,7 +57,7 @@ public class ServletTestExecutionListenerTestNGIntegrationTests extends Abstract
* @see #ensureMocksAreReinjectedBetweenTests_2
*/
@Test
- public void ensureMocksAreReinjectedBetweenTests_1() {
+ void ensureMocksAreReinjectedBetweenTests_1() {
assertInjectedServletRequestEqualsRequestInRequestContextHolder();
}
@@ -67,7 +67,7 @@ public class ServletTestExecutionListenerTestNGIntegrationTests extends Abstract
* @see #ensureMocksAreReinjectedBetweenTests_1
*/
@Test
- public void ensureMocksAreReinjectedBetweenTests_2() {
+ void ensureMocksAreReinjectedBetweenTests_2() {
assertInjectedServletRequestEqualsRequestInRequestContextHolder();
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/web/TestNGSpringContextWebTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/web/TestNGSpringContextWebTests.java
index 9092c3fe..c14c0f24 100644
--- a/spring-test/src/test/java/org/springframework/test/context/testng/web/TestNGSpringContextWebTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/testng/web/TestNGSpringContextWebTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -52,34 +52,34 @@ public class TestNGSpringContextWebTests extends AbstractTestNGSpringContextTest
static class Config {
@Bean
- public String foo() {
+ String foo() {
return "enigma";
}
}
- protected ServletContext servletContext;
+ ServletContext servletContext;
@Autowired
- protected WebApplicationContext wac;
+ WebApplicationContext wac;
@Autowired
- protected MockServletContext mockServletContext;
+ MockServletContext mockServletContext;
@Autowired
- protected MockHttpServletRequest request;
+ MockHttpServletRequest request;
@Autowired
- protected MockHttpServletResponse response;
+ MockHttpServletResponse response;
@Autowired
- protected MockHttpSession session;
+ MockHttpSession session;
@Autowired
- protected ServletWebRequest webRequest;
+ ServletWebRequest webRequest;
@Autowired
- protected String foo;
+ String foo;
@Override
@@ -88,7 +88,7 @@ public class TestNGSpringContextWebTests extends AbstractTestNGSpringContextTest
}
@Test
- public void basicWacFeatures() throws Exception {
+ void basicWacFeatures() throws Exception {
assertNotNull("ServletContext should be set in the WAC.", wac.getServletContext());
assertNotNull("ServletContext should have been set via ServletContextAware.", servletContext);
@@ -112,7 +112,7 @@ public class TestNGSpringContextWebTests extends AbstractTestNGSpringContextTest
}
@Test
- public void fooEnigmaAutowired() {
+ void fooEnigmaAutowired() {
assertEquals("enigma", foo);
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java
new file mode 100644
index 00000000..d9cd1526
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2002-2016 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.context.transaction;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.jdbc.JdbcTestUtils;
+import org.springframework.test.transaction.TransactionTestUtils;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that ensure that <em>primary</em> transaction managers
+ * are supported.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ * @see org.springframework.test.context.jdbc.PrimaryDataSourceTests
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@DirtiesContext
+public class PrimaryTransactionManagerTests {
+
+ @Configuration
+ static class Config {
+
+ @Primary
+ @Bean
+ public PlatformTransactionManager primaryTransactionManager() {
+ return new DataSourceTransactionManager(dataSource1());
+ }
+
+ @Bean
+ public PlatformTransactionManager additionalTransactionManager() {
+ return new DataSourceTransactionManager(dataSource2());
+ }
+
+ @Bean
+ public DataSource dataSource1() {
+ // @formatter:off
+ return new EmbeddedDatabaseBuilder()
+ .generateUniqueName(true)
+ .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")
+ .build();
+ // @formatter:on
+ }
+
+ @Bean
+ public DataSource dataSource2() {
+ return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
+ }
+
+ }
+
+
+ private JdbcTemplate jdbcTemplate;
+
+
+ @Autowired
+ public void setDataSource(DataSource dataSource1) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource1);
+ }
+
+ @BeforeTransaction
+ public void beforeTransaction() {
+ assertNumUsers(0);
+ }
+
+ @Test
+ @Transactional
+ public void transactionalTest() {
+ TransactionTestUtils.assertInTransaction(true);
+
+ ClassPathResource resource = new ClassPathResource("/org/springframework/test/context/jdbc/data.sql");
+ new ResourceDatabasePopulator(resource).execute(jdbcTemplate.getDataSource());
+
+ assertNumUsers(1);
+ }
+
+ @AfterTransaction
+ public void afterTransaction() {
+ assertNumUsers(0);
+ }
+
+ private void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected,
+ JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java
index 8d4e015f..82cb0081 100644
--- a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -37,7 +37,6 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.SimpleTransactionStatus;
import static org.hamcrest.CoreMatchers.*;
-
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.transaction.annotation.Propagation.*;
@@ -55,6 +54,7 @@ public class TransactionalTestExecutionListenerTests {
private final TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() {
+ @Override
protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
return tm;
}
@@ -82,10 +82,10 @@ public class TransactionalTestExecutionListenerTests {
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("transactionalTest"));
- assertFalse(instance.invoked);
+ assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
- assertEquals(invokedInTx, instance.invoked);
+ assertEquals(invokedInTx, instance.invoked());
}
private void assertBeforeTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz)
@@ -95,10 +95,10 @@ public class TransactionalTestExecutionListenerTests {
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("nonTransactionalTest"));
- assertFalse(instance.invoked);
+ assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
- assertFalse(instance.invoked);
+ assertFalse("callback should not have been invoked", instance.invoked());
}
private void assertAfterTestMethod(Class<? extends Invocable> clazz) throws Exception {
@@ -114,11 +114,12 @@ public class TransactionalTestExecutionListenerTests {
given(tm.getTransaction(BDDMockito.any(TransactionDefinition.class))).willReturn(new SimpleTransactionStatus());
- assertFalse(instance.invoked);
+ assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
+ assertFalse("callback should not have been invoked", instance.invoked());
listener.afterTestMethod(testContext);
- assertTrue(instance.invoked);
+ assertTrue("callback should have been invoked", instance.invoked());
}
private void assertAfterTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
@@ -127,11 +128,11 @@ public class TransactionalTestExecutionListenerTests {
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("nonTransactionalTest"));
- assertFalse(instance.invoked);
+ assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
listener.afterTestMethod(testContext);
- assertFalse(instance.invoked);
+ assertFalse("callback should not have been invoked", instance.invoked());
}
private void assertTransactionConfigurationAttributes(Class<?> clazz, String transactionManagerName,
@@ -174,7 +175,7 @@ public class TransactionalTestExecutionListenerTests {
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("transactionalTest"));
- assertFalse(instance.invoked);
+ assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
try {
@@ -245,6 +246,16 @@ public class TransactionalTestExecutionListenerTests {
}
@Test
+ public void beforeTestMethodWithBeforeTransactionDeclaredAsInterfaceDefaultMethod() throws Exception {
+ assertBeforeTestMethod(BeforeTransactionDeclaredAsInterfaceDefaultMethodTestCase.class);
+ }
+
+ @Test
+ public void afterTestMethodWithAfterTransactionDeclaredAsInterfaceDefaultMethod() throws Exception {
+ assertAfterTestMethod(AfterTransactionDeclaredAsInterfaceDefaultMethodTestCase.class);
+ }
+
+ @Test
public void retrieveConfigurationAttributesWithMissingTransactionConfiguration() throws Exception {
assertTransactionConfigurationAttributes(MissingTransactionConfigurationTestCase.class, "", true);
}
@@ -344,6 +355,16 @@ public class TransactionalTestExecutionListenerTests {
}
@Test
+ public void isRollbackWithClassLevelRollbackWithExplicitValueOnTestInterface() throws Exception {
+ assertIsRollback(ClassLevelRollbackWithExplicitValueOnTestInterfaceTestCase.class, false);
+ }
+
+ @Test
+ public void isRollbackWithClassLevelRollbackViaMetaAnnotationOnTestInterface() throws Exception {
+ assertIsRollback(ClassLevelRollbackViaMetaAnnotationOnTestInterfaceTestCase.class, false);
+ }
+
+ @Test
public void isRollbackWithRollbackAndTransactionConfigurationDeclaredAtClassLevel() throws Exception {
Class<?> clazz = ClassLevelRollbackAndTransactionConfigurationTestCase.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
@@ -388,17 +409,35 @@ public class TransactionalTestExecutionListenerTests {
String transactionManager() default "metaTxMgr";
}
- private static abstract class Invocable {
+ private interface Invocable {
+
+ void invoked(boolean invoked);
+
+ boolean invoked();
+ }
+
+ private static class AbstractInvocable implements Invocable {
boolean invoked = false;
+
+
+ @Override
+ public void invoked(boolean invoked) {
+ this.invoked = invoked;
+ }
+
+ @Override
+ public boolean invoked() {
+ return this.invoked;
+ }
}
@Transactional
- static class TransactionalDeclaredOnClassLocallyTestCase extends Invocable {
+ static class TransactionalDeclaredOnClassLocallyTestCase extends AbstractInvocable {
@BeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
public void transactionalTest() {
@@ -406,11 +445,11 @@ public class TransactionalTestExecutionListenerTests {
}
}
- static class TransactionalDeclaredOnMethodLocallyTestCase extends Invocable {
+ static class TransactionalDeclaredOnMethodLocallyTestCase extends AbstractInvocable {
@BeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
@Transactional
@@ -424,11 +463,11 @@ public class TransactionalTestExecutionListenerTests {
}
@MetaTransactional
- static class TransactionalDeclaredOnClassViaMetaAnnotationTestCase extends Invocable {
+ static class TransactionalDeclaredOnClassViaMetaAnnotationTestCase extends AbstractInvocable {
@BeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
public void transactionalTest() {
@@ -436,11 +475,11 @@ public class TransactionalTestExecutionListenerTests {
}
}
- static class TransactionalDeclaredOnMethodViaMetaAnnotationTestCase extends Invocable {
+ static class TransactionalDeclaredOnMethodViaMetaAnnotationTestCase extends AbstractInvocable {
@BeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
@MetaTransactional
@@ -454,11 +493,11 @@ public class TransactionalTestExecutionListenerTests {
}
@MetaTxWithOverride(propagation = NOT_SUPPORTED)
- static class TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase extends Invocable {
+ static class TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase extends AbstractInvocable {
@BeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
public void transactionalTest() {
@@ -466,11 +505,11 @@ public class TransactionalTestExecutionListenerTests {
}
}
- static class TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase extends Invocable {
+ static class TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase extends AbstractInvocable {
@BeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
@MetaTxWithOverride(propagation = NOT_SUPPORTED)
@@ -483,11 +522,11 @@ public class TransactionalTestExecutionListenerTests {
}
}
- static class BeforeTransactionDeclaredLocallyTestCase extends Invocable {
+ static class BeforeTransactionDeclaredLocallyTestCase extends AbstractInvocable {
@BeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
@Transactional
@@ -500,11 +539,11 @@ public class TransactionalTestExecutionListenerTests {
}
}
- static class BeforeTransactionDeclaredViaMetaAnnotationTestCase extends Invocable {
+ static class BeforeTransactionDeclaredViaMetaAnnotationTestCase extends AbstractInvocable {
@MetaBeforeTransaction
public void beforeTransaction() {
- invoked = true;
+ invoked(true);
}
@Transactional
@@ -517,11 +556,11 @@ public class TransactionalTestExecutionListenerTests {
}
}
- static class AfterTransactionDeclaredLocallyTestCase extends Invocable {
+ static class AfterTransactionDeclaredLocallyTestCase extends AbstractInvocable {
@AfterTransaction
public void afterTransaction() {
- invoked = true;
+ invoked(true);
}
@Transactional
@@ -534,12 +573,54 @@ public class TransactionalTestExecutionListenerTests {
}
}
- static class AfterTransactionDeclaredViaMetaAnnotationTestCase extends Invocable {
+ static class AfterTransactionDeclaredViaMetaAnnotationTestCase extends AbstractInvocable {
@MetaAfterTransaction
public void afterTransaction() {
- invoked = true;
+ invoked(true);
+ }
+
+ @Transactional
+ public void transactionalTest() {
+ /* no-op */
+ }
+
+ public void nonTransactionalTest() {
+ /* no-op */
+ }
+ }
+
+ interface BeforeTransactionInterface extends Invocable {
+
+ @BeforeTransaction
+ default void beforeTransaction() {
+ invoked(true);
}
+ }
+
+ interface AfterTransactionInterface extends Invocable {
+
+ @AfterTransaction
+ default void afterTransaction() {
+ invoked(true);
+ }
+ }
+
+ static class BeforeTransactionDeclaredAsInterfaceDefaultMethodTestCase extends AbstractInvocable
+ implements BeforeTransactionInterface {
+
+ @Transactional
+ public void transactionalTest() {
+ /* no-op */
+ }
+
+ public void nonTransactionalTest() {
+ /* no-op */
+ }
+ }
+
+ static class AfterTransactionDeclaredAsInterfaceDefaultMethodTestCase extends AbstractInvocable
+ implements AfterTransactionInterface {
@Transactional
public void transactionalTest() {
@@ -642,4 +723,25 @@ public class TransactionalTestExecutionListenerTests {
}
}
+ @Rollback(false)
+ interface RollbackFalseTestInterface {
+ }
+
+ static class ClassLevelRollbackWithExplicitValueOnTestInterfaceTestCase implements RollbackFalseTestInterface {
+
+ public void test() {
+ }
+ }
+
+ @Commit
+ interface RollbackFalseViaMetaAnnotationTestInterface {
+ }
+
+ static class ClassLevelRollbackViaMetaAnnotationOnTestInterfaceTestCase
+ implements RollbackFalseViaMetaAnnotationTestInterface {
+
+ public void test() {
+ }
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerTests.java
index 0353a870..55201245 100644
--- a/spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/web/ServletTestExecutionListenerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -38,11 +38,12 @@ import static org.springframework.test.context.web.ServletTestExecutionListener.
* Unit tests for {@link ServletTestExecutionListener}.
*
* @author Sam Brannen
+ * @author Phillip Webb
* @since 3.2.6
*/
public class ServletTestExecutionListenerTests {
- private static final String SET_UP_OUTSIDE_OF_STEL = "SET_UP_OUTSIDE_OF_STEL";
+ private static final String SET_UP_OUTSIDE_OF_STEL = "setUpOutsideOfStel";
private final WebApplicationContext wac = mock(WebApplicationContext.class);
private final MockServletContext mockServletContext = new MockServletContext();
@@ -50,30 +51,6 @@ public class ServletTestExecutionListenerTests {
private final ServletTestExecutionListener listener = new ServletTestExecutionListener();
- private void assertAttributesAvailable() {
- assertNotNull("request attributes should be available", RequestContextHolder.getRequestAttributes());
- }
-
- private void assertAttributesNotAvailable() {
- assertNull("request attributes should not be available", RequestContextHolder.getRequestAttributes());
- }
-
- private void assertAttributeExists() {
- RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
- assertNotNull("request attributes should exist", requestAttributes);
- Object setUpOutsideOfStel = requestAttributes.getAttribute(SET_UP_OUTSIDE_OF_STEL,
- RequestAttributes.SCOPE_REQUEST);
- assertNotNull(SET_UP_OUTSIDE_OF_STEL + " should exist as a request attribute", setUpOutsideOfStel);
- }
-
- private void assertAttributeDoesNotExist() {
- RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
- assertNotNull("request attributes should exist", requestAttributes);
- Object setUpOutsideOfStel = requestAttributes.getAttribute(SET_UP_OUTSIDE_OF_STEL,
- RequestAttributes.SCOPE_REQUEST);
- assertNull(SET_UP_OUTSIDE_OF_STEL + " should NOT exist as a request attribute", setUpOutsideOfStel);
- }
-
@Before
public void setUp() {
given(wac.getServletContext()).willReturn(mockServletContext);
@@ -86,7 +63,7 @@ public class ServletTestExecutionListenerTests {
request.setAttribute(SET_UP_OUTSIDE_OF_STEL, "true");
RequestContextHolder.setRequestAttributes(servletWebRequest);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
}
@Test
@@ -95,16 +72,16 @@ public class ServletTestExecutionListenerTests {
given(testContext.getApplicationContext()).willReturn(mock(ApplicationContext.class));
listener.beforeTestClass(testContext);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
listener.prepareTestInstance(testContext);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
listener.beforeTestMethod(testContext);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
listener.afterTestMethod(testContext);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
}
@Test
@@ -112,22 +89,22 @@ public class ServletTestExecutionListenerTests {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(LegacyWebTestCase.class);
RequestContextHolder.resetRequestAttributes();
- assertAttributesNotAvailable();
+ assertRequestAttributesDoNotExist();
listener.beforeTestClass(testContext);
listener.prepareTestInstance(testContext);
- assertAttributesNotAvailable();
+ assertRequestAttributesDoNotExist();
verify(testContext, times(0)).setAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
given(testContext.getAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE)).willReturn(null);
listener.beforeTestMethod(testContext);
- assertAttributesNotAvailable();
+ assertRequestAttributesDoNotExist();
verify(testContext, times(0)).setAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
listener.afterTestMethod(testContext);
verify(testContext, times(1)).removeAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE);
- assertAttributesNotAvailable();
+ assertRequestAttributesDoNotExist();
}
@Test
@@ -135,21 +112,21 @@ public class ServletTestExecutionListenerTests {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(LegacyWebTestCase.class);
listener.beforeTestClass(testContext);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
listener.prepareTestInstance(testContext);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
verify(testContext, times(0)).setAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
given(testContext.getAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE)).willReturn(null);
listener.beforeTestMethod(testContext);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
verify(testContext, times(0)).setAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
given(testContext.getAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE)).willReturn(null);
listener.afterTestMethod(testContext);
verify(testContext, times(1)).removeAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE);
- assertAttributeExists();
+ assertSetUpOutsideOfStelAttributeExists();
}
@Test
@@ -158,7 +135,7 @@ public class ServletTestExecutionListenerTests {
RequestContextHolder.resetRequestAttributes();
listener.beforeTestClass(testContext);
- assertAttributesNotAvailable();
+ assertRequestAttributesDoNotExist();
assertWebAppConfigTestCase();
}
@@ -168,28 +145,70 @@ public class ServletTestExecutionListenerTests {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(AtWebAppConfigWebTestCase.class);
listener.beforeTestClass(testContext);
- assertAttributesAvailable();
+ assertRequestAttributesExist();
assertWebAppConfigTestCase();
}
+ /**
+ * @since 4.3
+ */
+ @Test
+ public void activateListenerWithoutExistingRequestAttributes() throws Exception {
+ BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(NoAtWebAppConfigWebTestCase.class);
+ given(testContext.getAttribute(ServletTestExecutionListener.ACTIVATE_LISTENER)).willReturn(true);
+
+ RequestContextHolder.resetRequestAttributes();
+ listener.beforeTestClass(testContext);
+ assertRequestAttributesDoNotExist();
+
+ assertWebAppConfigTestCase();
+ }
+
+
+ private RequestAttributes assertRequestAttributesExist() {
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ assertNotNull("request attributes should exist", requestAttributes);
+ return requestAttributes;
+ }
+
+ private void assertRequestAttributesDoNotExist() {
+ assertNull("request attributes should not exist", RequestContextHolder.getRequestAttributes());
+ }
+
+ private void assertSetUpOutsideOfStelAttributeExists() {
+ RequestAttributes requestAttributes = assertRequestAttributesExist();
+ Object setUpOutsideOfStel = requestAttributes.getAttribute(SET_UP_OUTSIDE_OF_STEL,
+ RequestAttributes.SCOPE_REQUEST);
+ assertNotNull(SET_UP_OUTSIDE_OF_STEL + " should exist as a request attribute", setUpOutsideOfStel);
+ }
+
+ private void assertSetUpOutsideOfStelAttributeDoesNotExist() {
+ RequestAttributes requestAttributes = assertRequestAttributesExist();
+ Object setUpOutsideOfStel = requestAttributes.getAttribute(SET_UP_OUTSIDE_OF_STEL,
+ RequestAttributes.SCOPE_REQUEST);
+ assertNull(SET_UP_OUTSIDE_OF_STEL + " should NOT exist as a request attribute", setUpOutsideOfStel);
+ }
+
private void assertWebAppConfigTestCase() throws Exception {
listener.prepareTestInstance(testContext);
- assertAttributeDoesNotExist();
+ assertRequestAttributesExist();
+ assertSetUpOutsideOfStelAttributeDoesNotExist();
verify(testContext, times(1)).setAttribute(POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
verify(testContext, times(1)).setAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
given(testContext.getAttribute(POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE)).willReturn(Boolean.TRUE);
given(testContext.getAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE)).willReturn(Boolean.TRUE);
listener.beforeTestMethod(testContext);
- assertAttributeDoesNotExist();
+ assertRequestAttributesExist();
+ assertSetUpOutsideOfStelAttributeDoesNotExist();
verify(testContext, times(1)).setAttribute(POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
verify(testContext, times(1)).setAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
listener.afterTestMethod(testContext);
verify(testContext).removeAttribute(POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE);
verify(testContext).removeAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE);
- assertAttributesNotAvailable();
+ assertRequestAttributesDoNotExist();
}
@@ -200,4 +219,7 @@ public class ServletTestExecutionListenerTests {
static class AtWebAppConfigWebTestCase {
}
+ static class NoAtWebAppConfigWebTestCase {
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/web/WebAppConfigurationBootstrapWithTests.java b/spring-test/src/test/java/org/springframework/test/context/web/WebAppConfigurationBootstrapWithTests.java
new file mode 100644
index 00000000..ad03bb81
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/web/WebAppConfigurationBootstrapWithTests.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002-2016 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.context.web;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.test.context.BootstrapWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfigurationBootstrapWithTests.CustomWebTestContextBootstrapper;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * JUnit-based integration tests that verify support for loading a
+ * {@link WebApplicationContext} with a custom {@link WebTestContextBootstrapper}.
+ *
+ * @author Sam Brannen
+ * @author Phillip Webb
+ * @since 4.3
+ */
+@RunWith(SpringRunner.class)
+@ContextConfiguration
+@WebAppConfiguration
+@BootstrapWith(CustomWebTestContextBootstrapper.class)
+public class WebAppConfigurationBootstrapWithTests {
+
+ @Autowired
+ WebApplicationContext wac;
+
+
+ @Test
+ public void webApplicationContextIsLoaded() {
+ // from: src/test/webapp/resources/Spring.js
+ Resource resource = wac.getResource("/resources/Spring.js");
+ assertNotNull(resource);
+ assertTrue(resource.exists());
+ }
+
+
+ @Configuration
+ static class Config {
+ }
+
+ /**
+ * Custom {@link WebTestContextBootstrapper} that requires {@code @WebAppConfiguration}
+ * but hard codes the resource base path.
+ */
+ static class CustomWebTestContextBootstrapper extends WebTestContextBootstrapper {
+
+ @Override
+ protected MergedContextConfiguration processMergedContextConfiguration(MergedContextConfiguration mergedConfig) {
+ return new WebMergedContextConfiguration(mergedConfig, "src/test/webapp");
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/web/socket/WebSocketServletServerContainerFactoryBeanTests.java b/spring-test/src/test/java/org/springframework/test/context/web/socket/WebSocketServletServerContainerFactoryBeanTests.java
new file mode 100644
index 00000000..f35b87f9
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/web/socket/WebSocketServletServerContainerFactoryBeanTests.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2002-2016 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.context.web.socket;
+
+import javax.websocket.server.ServerContainer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that validate support for {@link ServletServerContainerFactoryBean}
+ * in conjunction with {@link WebAppConfiguration @WebAppConfiguration} and the
+ * Spring TestContext Framework.
+ *
+ * @author Sam Brannen
+ * @since 4.3.1
+ */
+@RunWith(SpringRunner.class)
+@WebAppConfiguration
+public class WebSocketServletServerContainerFactoryBeanTests {
+
+ @Autowired
+ ServerContainer serverContainer;
+
+
+ @Test
+ public void servletServerContainerFactoryBeanSupport() {
+ assertEquals(42, serverContainer.getDefaultMaxTextMessageBufferSize());
+ }
+
+
+ @Configuration
+ @EnableWebSocket
+ static class WebSocketConfig {
+
+ @Bean
+ ServletServerContainerFactoryBean createWebSocketContainer() {
+ ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
+ container.setMaxTextMessageBufferSize(42);
+ return container;
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java b/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java
index 03db1d60..3416f215 100644
--- a/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -25,10 +25,13 @@ import java.lang.annotation.Target;
import org.junit.Test;
+import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
+import org.springframework.test.util.MetaAnnotationUtils.UntypedAnnotationDescriptor;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.Assert.*;
@@ -112,10 +115,27 @@ public class MetaAnnotationUtilsTests {
@Test
public void findAnnotationDescriptorWithInheritedAnnotationOnInterface() throws Exception {
// Note: @Transactional is inherited
- assertEquals(InheritedAnnotationInterface.class,
- findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass());
- assertNull(findAnnotationDescriptor(SubInheritedAnnotationInterface.class, Transactional.class));
- assertNull(findAnnotationDescriptor(SubSubInheritedAnnotationInterface.class, Transactional.class));
+ Transactional rawAnnotation = InheritedAnnotationInterface.class.getAnnotation(Transactional.class);
+
+ AnnotationDescriptor<Transactional> descriptor;
+
+ descriptor = findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class);
+ assertNotNull(descriptor);
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+
+ descriptor = findAnnotationDescriptor(SubInheritedAnnotationInterface.class, Transactional.class);
+ assertNotNull(descriptor);
+ assertEquals(SubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+
+ descriptor = findAnnotationDescriptor(SubSubInheritedAnnotationInterface.class, Transactional.class);
+ assertNotNull(descriptor);
+ assertEquals(SubSubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
}
@Test
@@ -130,9 +150,21 @@ public class MetaAnnotationUtilsTests {
@Test
public void findAnnotationDescriptorForNonInheritedAnnotationOnInterface() throws Exception {
// Note: @Order is not inherited.
- assertEquals(NonInheritedAnnotationInterface.class,
- findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass());
- assertNull(findAnnotationDescriptor(SubNonInheritedAnnotationInterface.class, Order.class));
+ Order rawAnnotation = NonInheritedAnnotationInterface.class.getAnnotation(Order.class);
+
+ AnnotationDescriptor<Order> descriptor;
+
+ descriptor = findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class);
+ assertNotNull(descriptor);
+ assertEquals(NonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+
+ descriptor = findAnnotationDescriptor(SubNonInheritedAnnotationInterface.class, Order.class);
+ assertNotNull(descriptor);
+ assertEquals(SubNonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
}
@Test
@@ -158,7 +190,17 @@ public class MetaAnnotationUtilsTests {
@Test
public void findAnnotationDescriptorForClassWithMetaAnnotatedInterface() {
- assertNull(findAnnotationDescriptor(ClassWithMetaAnnotatedInterface.class, Component.class));
+ Component rawAnnotation = AnnotationUtils.findAnnotation(ClassWithMetaAnnotatedInterface.class,
+ Component.class);
+
+ AnnotationDescriptor<Component> descriptor;
+
+ descriptor = findAnnotationDescriptor(ClassWithMetaAnnotatedInterface.class, Component.class);
+ assertNotNull(descriptor);
+ assertEquals(ClassWithMetaAnnotatedInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(Meta1.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+ assertEquals(Meta1.class, descriptor.getComposedAnnotation().annotationType());
}
@Test
@@ -253,11 +295,27 @@ public class MetaAnnotationUtilsTests {
@SuppressWarnings("unchecked")
public void findAnnotationDescriptorForTypesWithInheritedAnnotationOnInterface() throws Exception {
// Note: @Transactional is inherited
- assertEquals(
- InheritedAnnotationInterface.class,
- findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass());
- assertNull(findAnnotationDescriptorForTypes(SubInheritedAnnotationInterface.class, Transactional.class));
- assertNull(findAnnotationDescriptorForTypes(SubSubInheritedAnnotationInterface.class, Transactional.class));
+ Transactional rawAnnotation = InheritedAnnotationInterface.class.getAnnotation(Transactional.class);
+
+ UntypedAnnotationDescriptor descriptor;
+
+ descriptor = findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class);
+ assertNotNull(descriptor);
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+
+ descriptor = findAnnotationDescriptorForTypes(SubInheritedAnnotationInterface.class, Transactional.class);
+ assertNotNull(descriptor);
+ assertEquals(SubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+
+ descriptor = findAnnotationDescriptorForTypes(SubSubInheritedAnnotationInterface.class, Transactional.class);
+ assertNotNull(descriptor);
+ assertEquals(SubSubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
}
@Test
@@ -274,10 +332,21 @@ public class MetaAnnotationUtilsTests {
@SuppressWarnings("unchecked")
public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnInterface() throws Exception {
// Note: @Order is not inherited.
- assertEquals(
- NonInheritedAnnotationInterface.class,
- findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass());
- assertNull(findAnnotationDescriptorForTypes(SubNonInheritedAnnotationInterface.class, Order.class));
+ Order rawAnnotation = NonInheritedAnnotationInterface.class.getAnnotation(Order.class);
+
+ UntypedAnnotationDescriptor descriptor;
+
+ descriptor = findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class);
+ assertNotNull(descriptor);
+ assertEquals(NonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+
+ descriptor = findAnnotationDescriptorForTypes(SubNonInheritedAnnotationInterface.class, Order.class);
+ assertNotNull(descriptor);
+ assertEquals(SubNonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
}
@Test
@@ -345,8 +414,18 @@ public class MetaAnnotationUtilsTests {
@Test
@SuppressWarnings("unchecked")
public void findAnnotationDescriptorForTypesForClassWithMetaAnnotatedInterface() {
- assertNull(findAnnotationDescriptorForTypes(ClassWithMetaAnnotatedInterface.class, Service.class,
- Component.class, Order.class, Transactional.class));
+ Component rawAnnotation = AnnotationUtils.findAnnotation(ClassWithMetaAnnotatedInterface.class,
+ Component.class);
+
+ UntypedAnnotationDescriptor descriptor;
+
+ descriptor = findAnnotationDescriptorForTypes(ClassWithMetaAnnotatedInterface.class, Service.class,
+ Component.class, Order.class, Transactional.class);
+ assertNotNull(descriptor);
+ assertEquals(ClassWithMetaAnnotatedInterface.class, descriptor.getRootDeclaringClass());
+ assertEquals(Meta1.class, descriptor.getDeclaringClass());
+ assertEquals(rawAnnotation, descriptor.getAnnotation());
+ assertEquals(Meta1.class, descriptor.getComposedAnnotation().annotationType());
}
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/util/ReflectionTestUtilsTests.java b/spring-test/src/test/java/org/springframework/test/util/ReflectionTestUtilsTests.java
index fa692ed9..3ea39f9b 100644
--- a/spring-test/src/test/java/org/springframework/test/util/ReflectionTestUtilsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/util/ReflectionTestUtilsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -22,9 +22,12 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.aop.support.AopUtils;
import org.springframework.test.util.subpackage.Component;
import org.springframework.test.util.subpackage.LegacyEntity;
import org.springframework.test.util.subpackage.Person;
+import org.springframework.test.util.subpackage.PersonEntity;
import org.springframework.test.util.subpackage.StaticFields;
import static org.hamcrest.CoreMatchers.*;
@@ -41,12 +44,14 @@ public class ReflectionTestUtilsTests {
private static final Float PI = new Float((float) 22 / 7);
- private final Person person = new Person();
+ private final Person person = new PersonEntity();
private final Component component = new Component();
+ private final LegacyEntity entity = new LegacyEntity();
+
@Rule
- public ExpectedException exception = ExpectedException.none();
+ public final ExpectedException exception = ExpectedException.none();
@Before
@@ -54,7 +59,6 @@ public class ReflectionTestUtilsTests {
StaticFields.reset();
}
-
@Test
public void setFieldWithNullTargetObject() throws Exception {
exception.expect(IllegalArgumentException.class);
@@ -106,6 +110,29 @@ public class ReflectionTestUtilsTests {
@Test
public void setFieldAndGetFieldForStandardUseCases() throws Exception {
+ assertSetFieldAndGetFieldBehavior(this.person);
+ }
+
+ @Test
+ public void setFieldAndGetFieldViaJdkDynamicProxy() throws Exception {
+ ProxyFactory pf = new ProxyFactory(this.person);
+ pf.addInterface(Person.class);
+ Person proxy = (Person) pf.getProxy();
+ assertTrue("Proxy is a JDK dynamic proxy", AopUtils.isJdkDynamicProxy(proxy));
+ assertSetFieldAndGetFieldBehaviorForProxy(proxy, this.person);
+ }
+
+ @Test
+ public void setFieldAndGetFieldViaCglibProxy() throws Exception {
+ ProxyFactory pf = new ProxyFactory(this.person);
+ pf.setProxyTargetClass(true);
+ Person proxy = (Person) pf.getProxy();
+ assertTrue("Proxy is a CGLIB proxy", AopUtils.isCglibProxy(proxy));
+ assertSetFieldAndGetFieldBehaviorForProxy(proxy, this.person);
+ }
+
+ private static void assertSetFieldAndGetFieldBehavior(Person person) {
+ // Set reflectively
setField(person, "id", new Long(99), long.class);
setField(person, "name", "Tom");
setField(person, "age", new Integer(42));
@@ -113,19 +140,33 @@ public class ReflectionTestUtilsTests {
setField(person, "likesPets", Boolean.TRUE);
setField(person, "favoriteNumber", PI, Number.class);
+ // Get reflectively
+ assertEquals(new Long(99), getField(person, "id"));
+ assertEquals("Tom", getField(person, "name"));
+ assertEquals(new Integer(42), getField(person, "age"));
+ assertEquals("blue", getField(person, "eyeColor"));
+ assertEquals(Boolean.TRUE, getField(person, "likesPets"));
+ assertEquals(PI, getField(person, "favoriteNumber"));
+
+ // Get directly
assertEquals("ID (private field in a superclass)", 99, person.getId());
assertEquals("name (protected field)", "Tom", person.getName());
assertEquals("age (private field)", 42, person.getAge());
assertEquals("eye color (package private field)", "blue", person.getEyeColor());
assertEquals("'likes pets' flag (package private boolean field)", true, person.likesPets());
assertEquals("'favorite number' (package field)", PI, person.getFavoriteNumber());
+ }
- assertEquals(new Long(99), getField(person, "id"));
- assertEquals("Tom", getField(person, "name"));
- assertEquals(new Integer(42), getField(person, "age"));
- assertEquals("blue", getField(person, "eyeColor"));
- assertEquals(Boolean.TRUE, getField(person, "likesPets"));
- assertEquals(PI, getField(person, "favoriteNumber"));
+ private static void assertSetFieldAndGetFieldBehaviorForProxy(Person proxy, Person target) {
+ assertSetFieldAndGetFieldBehavior(proxy);
+
+ // Get directly from Target
+ assertEquals("ID (private field in a superclass)", 99, target.getId());
+ assertEquals("name (protected field)", "Tom", target.getName());
+ assertEquals("age (private field)", 42, target.getAge());
+ assertEquals("eye color (package private field)", "blue", target.getEyeColor());
+ assertEquals("'likes pets' flag (package private boolean field)", true, target.likesPets());
+ assertEquals("'favorite number' (package field)", PI, target.getFavoriteNumber());
}
@Test
@@ -163,17 +204,6 @@ public class ReflectionTestUtilsTests {
setField(person, "likesPets", null, boolean.class);
}
- /**
- * Verifies behavior requested in <a href="https://jira.spring.io/browse/SPR-9571">SPR-9571</a>.
- */
- @Test
- public void setFieldOnLegacyEntityWithSideEffectsInToString() {
- String testCollaborator = "test collaborator";
- LegacyEntity entity = new LegacyEntity();
- setField(entity, "collaborator", testCollaborator, Object.class);
- assertTrue(entity.toString().contains(testCollaborator));
- }
-
@Test
public void setStaticFieldViaClass() throws Exception {
setField(StaticFields.class, "publicField", "xxx");
@@ -359,4 +389,37 @@ public class ReflectionTestUtilsTests {
invokeMethod(component, "configure", new Integer(42), "enigma", "baz", "quux");
}
+ @Test // SPR-14363
+ public void getFieldOnLegacyEntityWithSideEffectsInToString() {
+ Object collaborator = getField(entity, "collaborator");
+ assertNotNull(collaborator);
+ }
+
+ @Test // SPR-9571 and SPR-14363
+ public void setFieldOnLegacyEntityWithSideEffectsInToString() {
+ String testCollaborator = "test collaborator";
+ setField(entity, "collaborator", testCollaborator, Object.class);
+ assertTrue(entity.toString().contains(testCollaborator));
+ }
+
+ @Test // SPR-14363
+ public void invokeMethodOnLegacyEntityWithSideEffectsInToString() {
+ invokeMethod(entity, "configure", new Integer(42), "enigma");
+ assertEquals("number should have been configured", new Integer(42), entity.getNumber());
+ assertEquals("text should have been configured", "enigma", entity.getText());
+ }
+
+ @Test // SPR-14363
+ public void invokeGetterMethodOnLegacyEntityWithSideEffectsInToString() {
+ Object collaborator = invokeGetterMethod(entity, "collaborator");
+ assertNotNull(collaborator);
+ }
+
+ @Test // SPR-14363
+ public void invokeSetterMethodOnLegacyEntityWithSideEffectsInToString() {
+ String testCollaborator = "test collaborator";
+ invokeSetterMethod(entity, "collaborator", testCollaborator);
+ assertTrue(entity.toString().contains(testCollaborator));
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntity.java b/spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntity.java
index 8c8ddfd4..72d4633f 100644
--- a/spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntity.java
+++ b/spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -31,17 +31,41 @@ public class LegacyEntity {
@Override
public String toString() {
- throw new RuntimeException(
+ throw new LegacyEntityException(
"Invoking toString() on the default collaborator causes an undesirable side effect");
- };
+ }
};
+ private Integer number;
+ private String text;
+
+
+ public void configure(Integer number, String text) {
+ this.number = number;
+ this.text = text;
+ }
+
+ public Integer getNumber() {
+ return this.number;
+ }
+
+ public String getText() {
+ return this.text;
+ }
+
+ public Object getCollaborator() {
+ return this.collaborator;
+ }
+
+ public void setCollaborator(Object collaborator) {
+ this.collaborator = collaborator;
+ }
@Override
public String toString() {
return new ToStringCreator(this)//
- .append("collaborator", this.collaborator)//
- .toString();
+ .append("collaborator", this.collaborator)//
+ .toString();
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntityException.java b/spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntityException.java
new file mode 100644
index 00000000..2318e90a
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/util/subpackage/LegacyEntityException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2016 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.util.subpackage;
+
+/**
+ * Exception thrown by a {@link LegacyEntity}.
+ *
+ * @author Sam Brannen
+ * @since 4.3.1
+ */
+@SuppressWarnings("serial")
+public class LegacyEntityException extends RuntimeException {
+
+ public LegacyEntityException(String message) {
+ super(message);
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/util/subpackage/PersistentEntity.java b/spring-test/src/test/java/org/springframework/test/util/subpackage/PersistentEntity.java
index 3ff3d91d..4f34e2e8 100644
--- a/spring-test/src/test/java/org/springframework/test/util/subpackage/PersistentEntity.java
+++ b/spring-test/src/test/java/org/springframework/test/util/subpackage/PersistentEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2011 the original author or authors.
+ * Copyright 2007-2016 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.
@@ -28,11 +28,12 @@ public abstract class PersistentEntity {
private long id;
- public final long getId() {
+ public long getId() {
return this.id;
}
- protected final void setId(long id) {
+ protected void setId(long id) {
this.id = id;
}
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/util/subpackage/Person.java b/spring-test/src/test/java/org/springframework/test/util/subpackage/Person.java
index b89f5616..20177ed0 100644
--- a/spring-test/src/test/java/org/springframework/test/util/subpackage/Person.java
+++ b/spring-test/src/test/java/org/springframework/test/util/subpackage/Person.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,85 +16,27 @@
package org.springframework.test.util.subpackage;
-import org.springframework.core.style.ToStringCreator;
-
/**
- * Concrete subclass of {@link PersistentEntity} representing a <em>person</em>
- * entity; intended for use in unit tests.
+ * Interface representing a <em>person</em> entity; intended for use in unit tests.
+ *
+ * <p>The introduction of an interface is necessary in order to test support for
+ * JDK dynamic proxies.
*
* @author Sam Brannen
- * @since 2.5
+ * @since 4.3
*/
-public class Person extends PersistentEntity {
-
- protected String name;
-
- private int age;
-
- String eyeColor;
-
- boolean likesPets = false;
-
- private Number favoriteNumber;
-
-
- public final String getName() {
- return this.name;
- }
-
- @SuppressWarnings("unused")
- private final void setName(final String name) {
- this.name = name;
- }
-
- public final int getAge() {
- return this.age;
- }
-
- protected final void setAge(final int age) {
- this.age = age;
- }
-
- public final String getEyeColor() {
- return this.eyeColor;
- }
-
- final void setEyeColor(final String eyeColor) {
- this.eyeColor = eyeColor;
- }
-
- public final boolean likesPets() {
- return this.likesPets;
- }
-
- protected final void setLikesPets(final boolean likesPets) {
- this.likesPets = likesPets;
- }
-
- public final Number getFavoriteNumber() {
- return this.favoriteNumber;
- }
-
- protected final void setFavoriteNumber(Number favoriteNumber) {
- this.favoriteNumber = favoriteNumber;
- }
-
- @Override
- public String toString() {
- return new ToStringCreator(this)
+public interface Person {
- .append("id", this.getId())
+ long getId();
- .append("name", this.name)
+ String getName();
- .append("age", this.age)
+ int getAge();
- .append("eyeColor", this.eyeColor)
+ String getEyeColor();
- .append("likesPets", this.likesPets)
+ boolean likesPets();
- .append("favoriteNumber", this.favoriteNumber)
+ Number getFavoriteNumber();
- .toString();
- }
}
diff --git a/spring-test/src/test/java/org/springframework/test/util/subpackage/PersonEntity.java b/spring-test/src/test/java/org/springframework/test/util/subpackage/PersonEntity.java
new file mode 100644
index 00000000..1cdff47b
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/util/subpackage/PersonEntity.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2002-2016 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.util.subpackage;
+
+import org.springframework.core.style.ToStringCreator;
+
+/**
+ * Concrete subclass of {@link PersistentEntity} representing a <em>person</em>
+ * entity; intended for use in unit tests.
+ *
+ * @author Sam Brannen
+ * @since 2.5
+ */
+public class PersonEntity extends PersistentEntity implements Person {
+
+ protected String name;
+
+ private int age;
+
+ String eyeColor;
+
+ boolean likesPets = false;
+
+ private Number favoriteNumber;
+
+
+ public String getName() {
+ return this.name;
+ }
+
+ @SuppressWarnings("unused")
+ private void setName(final String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return this.age;
+ }
+
+ protected void setAge(final int age) {
+ this.age = age;
+ }
+
+ public String getEyeColor() {
+ return this.eyeColor;
+ }
+
+ void setEyeColor(final String eyeColor) {
+ this.eyeColor = eyeColor;
+ }
+
+ public boolean likesPets() {
+ return this.likesPets;
+ }
+
+ protected void setLikesPets(final boolean likesPets) {
+ this.likesPets = likesPets;
+ }
+
+ public Number getFavoriteNumber() {
+ return this.favoriteNumber;
+ }
+
+ protected void setFavoriteNumber(Number favoriteNumber) {
+ this.favoriteNumber = favoriteNumber;
+ }
+
+ @Override
+ public String toString() {
+ // @formatter:off
+ return new ToStringCreator(this)
+ .append("id", this.getId())
+ .append("name", this.name)
+ .append("age", this.age)
+ .append("eyeColor", this.eyeColor)
+ .append("likesPets", this.likesPets)
+ .append("favoriteNumber", this.favoriteNumber)
+ .toString();
+ // @formatter:on
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/DefaultRequestExpectationTests.java b/spring-test/src/test/java/org/springframework/test/web/client/DefaultRequestExpectationTests.java
new file mode 100644
index 00000000..aea4f2d8
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/web/client/DefaultRequestExpectationTests.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
+
+import static junit.framework.TestCase.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.http.HttpMethod.GET;
+import static org.springframework.http.HttpMethod.POST;
+import static org.springframework.test.web.client.ExpectedCount.once;
+import static org.springframework.test.web.client.ExpectedCount.times;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+/**
+ * Unit tests for {@link DefaultRequestExpectation}.
+ * @author Rossen Stoyanchev
+ */
+public class DefaultRequestExpectationTests {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+
+ @Test
+ public void match() throws Exception {
+ RequestExpectation expectation = new DefaultRequestExpectation(once(), requestTo("/foo"));
+ expectation.match(createRequest(GET, "/foo"));
+ }
+
+ @Test
+ public void matchWithFailedExpection() throws Exception {
+ RequestExpectation expectation = new DefaultRequestExpectation(once(), requestTo("/foo"));
+ expectation.andExpect(method(POST));
+
+ this.thrown.expectMessage("Unexpected HttpMethod expected:<POST> but was:<GET>");
+ expectation.match(createRequest(GET, "/foo"));
+ }
+
+ @Test
+ public void hasRemainingCount() throws Exception {
+ RequestExpectation expectation = new DefaultRequestExpectation(times(2), requestTo("/foo"));
+ expectation.andRespond(withSuccess());
+
+ expectation.createResponse(createRequest(GET, "/foo"));
+ assertTrue(expectation.hasRemainingCount());
+
+ expectation.createResponse(createRequest(GET, "/foo"));
+ assertFalse(expectation.hasRemainingCount());
+ }
+
+ @Test
+ public void isSatisfied() throws Exception {
+ RequestExpectation expectation = new DefaultRequestExpectation(times(2), requestTo("/foo"));
+ expectation.andRespond(withSuccess());
+
+ expectation.createResponse(createRequest(GET, "/foo"));
+ assertFalse(expectation.isSatisfied());
+
+ expectation.createResponse(createRequest(GET, "/foo"));
+ assertTrue(expectation.isSatisfied());
+ }
+
+
+
+ private ClientHttpRequest createRequest(HttpMethod method, String url) {
+ try {
+ return new MockAsyncClientHttpRequest(method, new URI(url));
+ }
+ catch (URISyntaxException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/MockClientHttpRequestFactoryTests.java b/spring-test/src/test/java/org/springframework/test/web/client/MockClientHttpRequestFactoryTests.java
deleted file mode 100644
index dfd3a714..00000000
--- a/spring-test/src/test/java/org/springframework/test/web/client/MockClientHttpRequestFactoryTests.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2002-2014 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.client;
-
-import java.net.URI;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import org.springframework.http.HttpMethod;
-import org.springframework.http.client.ClientHttpRequest;
-import org.springframework.http.client.ClientHttpRequestFactory;
-import org.springframework.web.client.RestTemplate;
-
-import static org.junit.Assert.*;
-import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
-
-/**
- * Tests for
- * {@link org.springframework.test.web.client.MockMvcClientHttpRequestFactory}.
- *
- * @author Rossen Stoyanchev
- */
-public class MockClientHttpRequestFactoryTests {
-
- private MockRestServiceServer server;
-
- private ClientHttpRequestFactory factory;
-
-
- @Before
- public void setup() {
- RestTemplate restTemplate = new RestTemplate();
- this.server = MockRestServiceServer.createServer(restTemplate);
- this.factory = restTemplate.getRequestFactory();
- }
-
- @Test
- public void createRequest() throws Exception {
- URI uri = new URI("/foo");
- ClientHttpRequest expected = (ClientHttpRequest) this.server.expect(anything());
- ClientHttpRequest actual = this.factory.createRequest(uri, HttpMethod.GET);
-
- assertSame(expected, actual);
- assertEquals(uri, actual.getURI());
- assertEquals(HttpMethod.GET, actual.getMethod());
- }
-
- @Test
- public void noFurtherRequestsExpected() throws Exception {
- try {
- this.factory.createRequest(new URI("/foo"), HttpMethod.GET);
- }
- catch (AssertionError error) {
- assertEquals("No further requests expected: HTTP GET /foo", error.getMessage());
- }
- }
-
- @Test
- public void verifyZeroExpected() throws Exception {
- this.server.verify();
- }
-
- @Test
- public void verifyExpectedEqualExecuted() throws Exception {
- this.server.expect(anything());
- this.server.expect(anything());
-
- this.factory.createRequest(new URI("/foo"), HttpMethod.GET);
- this.factory.createRequest(new URI("/bar"), HttpMethod.POST);
- }
-
- @Test
- public void verifyMoreExpected() throws Exception {
- this.server.expect(anything());
- this.server.expect(anything());
-
- this.factory.createRequest(new URI("/foo"), HttpMethod.GET);
-
- try {
- this.server.verify();
- }
- catch (AssertionError error) {
- assertTrue(error.getMessage(), error.getMessage().contains("1 out of 2 were executed"));
- }
- }
-
-}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java b/spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java
new file mode 100644
index 00000000..ddf8b26f
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import org.junit.Test;
+
+import org.springframework.test.web.client.MockRestServiceServer.MockRestServiceServerBuilder;
+import org.springframework.web.client.RestTemplate;
+
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+/**
+ * Unit tests for {@link MockRestServiceServer}.
+ * @author Rossen Stoyanchev
+ */
+public class MockRestServiceServerTests {
+
+ private RestTemplate restTemplate = new RestTemplate();
+
+
+ @Test
+ public void buildMultipleTimes() throws Exception {
+ MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(this.restTemplate);
+
+ MockRestServiceServer server = builder.build();
+ server.expect(requestTo("/foo")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/foo", Void.class);
+ server.verify();
+
+ server = builder.ignoreExpectOrder(true).build();
+ server.expect(requestTo("/foo")).andRespond(withSuccess());
+ server.expect(requestTo("/bar")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/bar", Void.class);
+ this.restTemplate.getForObject("/foo", Void.class);
+ server.verify();
+
+ server = builder.build();
+ server.expect(requestTo("/bar")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/bar", Void.class);
+ server.verify();
+ }
+
+ @Test(expected = AssertionError.class)
+ public void exactExpectOrder() throws Exception {
+ MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate)
+ .ignoreExpectOrder(false).build();
+
+ server.expect(requestTo("/foo")).andRespond(withSuccess());
+ server.expect(requestTo("/bar")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/bar", Void.class);
+ }
+
+ @Test
+ public void ignoreExpectOrder() throws Exception {
+ MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate)
+ .ignoreExpectOrder(true).build();
+
+ server.expect(requestTo("/foo")).andRespond(withSuccess());
+ server.expect(requestTo("/bar")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/bar", Void.class);
+ this.restTemplate.getForObject("/foo", Void.class);
+ server.verify();
+ }
+
+ @Test
+ public void resetAndReuseServer() throws Exception {
+ MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate).build();
+
+ server.expect(requestTo("/foo")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/foo", Void.class);
+ server.verify();
+ server.reset();
+
+ server.expect(requestTo("/bar")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/bar", Void.class);
+ server.verify();
+ }
+
+ @Test
+ public void resetAndReuseServerWithUnorderedExpectationManager() throws Exception {
+ MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate)
+ .ignoreExpectOrder(true).build();
+
+ server.expect(requestTo("/foo")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/foo", Void.class);
+ server.verify();
+ server.reset();
+
+ server.expect(requestTo("/foo")).andRespond(withSuccess());
+ server.expect(requestTo("/bar")).andRespond(withSuccess());
+ this.restTemplate.getForObject("/bar", Void.class);
+ this.restTemplate.getForObject("/foo", Void.class);
+ server.verify();
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java b/spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java
new file mode 100644
index 00000000..d6f2b9cb
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
+
+import static org.junit.Assert.assertEquals;
+import static org.springframework.http.HttpMethod.GET;
+import static org.springframework.http.HttpMethod.POST;
+import static org.springframework.test.web.client.ExpectedCount.max;
+import static org.springframework.test.web.client.ExpectedCount.min;
+import static org.springframework.test.web.client.ExpectedCount.once;
+import static org.springframework.test.web.client.ExpectedCount.times;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+/**
+ * Unit tests for {@link SimpleRequestExpectationManager}.
+ * @author Rossen Stoyanchev
+ */
+public class SimpleRequestExpectationManagerTests {
+
+ private SimpleRequestExpectationManager manager = new SimpleRequestExpectationManager();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+
+ @Test
+ public void unexpectedRequest() throws Exception {
+ try {
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ }
+ catch (AssertionError error) {
+ assertEquals("No further requests expected: HTTP GET /foo\n" +
+ "0 request(s) executed.\n", error.getMessage());
+ }
+ }
+
+ @Test
+ public void zeroExpectedRequests() throws Exception {
+ this.manager.verify();
+ }
+
+ @Test
+ public void sequentialRequests() throws Exception {
+ this.manager.expectRequest(once(), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(once(), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.verify();
+ }
+
+ @Test
+ public void sequentialRequestsTooMany() throws Exception {
+ this.manager.expectRequest(max(1), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(max(1), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.thrown.expectMessage("No further requests expected: HTTP GET /baz\n" +
+ "2 request(s) executed:\n" +
+ "GET /foo\n" +
+ "GET /bar\n");
+
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/baz"));
+ }
+
+ @Test
+ public void sequentialRequestsTooFew() throws Exception {
+ this.manager.expectRequest(min(1), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(min(1), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.thrown.expectMessage("Further request(s) expected leaving 1 unsatisfied expectation(s).\n" +
+ "1 request(s) executed:\nGET /foo\n");
+
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.verify();
+ }
+
+ @Test
+ public void repeatedRequests() throws Exception {
+ this.manager.expectRequest(times(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(times(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.verify();
+ }
+
+ @Test
+ public void repeatedRequestsTooMany() throws Exception {
+ this.manager.expectRequest(max(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(max(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.thrown.expectMessage("No further requests expected: HTTP GET /foo\n" +
+ "4 request(s) executed:\n" +
+ "GET /foo\n" +
+ "GET /bar\n" +
+ "GET /foo\n" +
+ "GET /bar\n");
+
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ }
+
+ @Test
+ public void repeatedRequestsTooFew() throws Exception {
+ this.manager.expectRequest(min(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(min(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.thrown.expectMessage("3 request(s) executed:\n" +
+ "GET /foo\n" +
+ "GET /bar\n" +
+ "GET /foo\n");
+
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.verify();
+ }
+
+ @Test
+ public void repeatedRequestsNotInOrder() throws Exception {
+ this.manager.expectRequest(times(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(times(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(times(2), requestTo("/baz")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.thrown.expectMessage("Unexpected HttpMethod expected:<GET> but was:<POST>");
+ this.manager.validateRequest(createRequest(POST, "/foo"));
+ }
+
+
+ private ClientHttpRequest createRequest(HttpMethod method, String url) {
+ try {
+ return new MockAsyncClientHttpRequest(method, new URI(url));
+ }
+ catch (URISyntaxException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/UnorderedRequestExpectationManagerTests.java b/spring-test/src/test/java/org/springframework/test/web/client/UnorderedRequestExpectationManagerTests.java
new file mode 100644
index 00000000..163f3865
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/web/client/UnorderedRequestExpectationManagerTests.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2002-2016 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.client;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
+
+import static org.junit.Assert.assertEquals;
+import static org.springframework.http.HttpMethod.GET;
+import static org.springframework.test.web.client.ExpectedCount.max;
+import static org.springframework.test.web.client.ExpectedCount.min;
+import static org.springframework.test.web.client.ExpectedCount.once;
+import static org.springframework.test.web.client.ExpectedCount.times;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+/**
+ * Unit tests for {@link UnorderedRequestExpectationManager}.
+ * @author Rossen Stoyanchev
+ */
+public class UnorderedRequestExpectationManagerTests {
+
+ private UnorderedRequestExpectationManager manager = new UnorderedRequestExpectationManager();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+
+ @Test
+ public void unexpectedRequest() throws Exception {
+ try {
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ }
+ catch (AssertionError error) {
+ assertEquals("No further requests expected: HTTP GET /foo\n" +
+ "0 request(s) executed.\n", error.getMessage());
+ }
+ }
+
+ @Test
+ public void zeroExpectedRequests() throws Exception {
+ this.manager.verify();
+ }
+
+ @Test
+ public void multipleRequests() throws Exception {
+ this.manager.expectRequest(once(), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(once(), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.verify();
+ }
+
+ @Test
+ public void repeatedRequests() throws Exception {
+ this.manager.expectRequest(times(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(times(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.verify();
+ }
+
+ @Test
+ public void repeatedRequestsTooMany() throws Exception {
+ this.manager.expectRequest(max(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(max(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.thrown.expectMessage("No further requests expected: HTTP GET /foo\n" +
+ "4 request(s) executed:\n" +
+ "GET /bar\n" +
+ "GET /foo\n" +
+ "GET /bar\n" +
+ "GET /foo\n");
+
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ }
+
+ @Test
+ public void repeatedRequestsTooFew() throws Exception {
+ this.manager.expectRequest(min(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
+ this.manager.expectRequest(min(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
+
+ this.thrown.expectMessage("3 request(s) executed:\n" +
+ "GET /bar\n" +
+ "GET /foo\n" +
+ "GET /foo\n");
+
+ this.manager.validateRequest(createRequest(GET, "/bar"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.validateRequest(createRequest(GET, "/foo"));
+ this.manager.verify();
+ }
+
+
+ private ClientHttpRequest createRequest(HttpMethod method, String url) {
+ try {
+ return new MockAsyncClientHttpRequest(method, new URI(url));
+ }
+ catch (URISyntaxException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java
index eada001d..de034c24 100644
--- a/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -15,11 +15,15 @@
*/
package org.springframework.test.web.client.match;
+import java.nio.charset.Charset;
+
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
import static org.hamcrest.Matchers.*;
@@ -32,11 +36,13 @@ public class ContentRequestMatchersTests {
private MockClientHttpRequest request;
+
@Before
public void setUp() {
this.request = new MockClientHttpRequest();
}
+
@Test
public void testContentType() throws Exception {
this.request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
@@ -45,14 +51,14 @@ public class ContentRequestMatchersTests {
MockRestRequestMatchers.content().contentType(MediaType.APPLICATION_JSON).match(this.request);
}
- @Test(expected=AssertionError.class)
+ @Test(expected = AssertionError.class)
public void testContentTypeNoMatch1() throws Exception {
this.request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
MockRestRequestMatchers.content().contentType("application/xml").match(this.request);
}
- @Test(expected=AssertionError.class)
+ @Test(expected = AssertionError.class)
public void testContentTypeNoMatch2() throws Exception {
this.request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
@@ -66,7 +72,7 @@ public class ContentRequestMatchersTests {
MockRestRequestMatchers.content().string("test").match(this.request);
}
- @Test(expected=AssertionError.class)
+ @Test(expected = AssertionError.class)
public void testStringNoMatch() throws Exception {
this.request.getBody().write("test".getBytes());
@@ -81,7 +87,7 @@ public class ContentRequestMatchersTests {
MockRestRequestMatchers.content().bytes(content).match(this.request);
}
- @Test(expected=AssertionError.class)
+ @Test(expected = AssertionError.class)
public void testBytesNoMatch() throws Exception {
this.request.getBody().write("test".getBytes());
@@ -89,6 +95,22 @@ public class ContentRequestMatchersTests {
}
@Test
+ public void testFormData() throws Exception {
+ String contentType = "application/x-www-form-urlencoded;charset=UTF-8";
+ String body = "name+1=value+1&name+2=value+A&name+2=value+B&name+3";
+
+ this.request.getHeaders().setContentType(MediaType.parseMediaType(contentType));
+ this.request.getBody().write(body.getBytes(Charset.forName("UTF-8")));
+
+ MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
+ map.add("name 1", "value 1");
+ map.add("name 2", "value A");
+ map.add("name 2", "value B");
+ map.add("name 3", null);
+ MockRestRequestMatchers.content().formData(map).match(this.request);
+ }
+
+ @Test
public void testXml() throws Exception {
String content = "<foo><bar>baz</bar><bar>bazz</bar></foo>";
this.request.getBody().write(content.getBytes());
@@ -96,7 +118,7 @@ public class ContentRequestMatchersTests {
MockRestRequestMatchers.content().xml(content).match(this.request);
}
- @Test(expected=AssertionError.class)
+ @Test(expected = AssertionError.class)
public void testXmlNoMatch() throws Exception {
this.request.getBody().write("<foo>11</foo>".getBytes());
@@ -111,7 +133,7 @@ public class ContentRequestMatchersTests {
MockRestRequestMatchers.content().node(hasXPath("/foo/bar")).match(this.request);
}
- @Test(expected=AssertionError.class)
+ @Test(expected = AssertionError.class)
public void testNodeMatcherNoMatch() throws Exception {
String content = "<foo><bar>baz</bar></foo>";
this.request.getBody().write(content.getBytes());
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/match/MockRestRequestMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/client/match/MockRestRequestMatchersTests.java
index 787020d6..58cad79c 100644
--- a/spring-test/src/test/java/org/springframework/test/web/client/match/MockRestRequestMatchersTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/client/match/MockRestRequestMatchersTests.java
@@ -91,7 +91,6 @@ public class MockRestRequestMatchersTests {
MockRestRequestMatchers.header("foo", "bad").match(this.request);
}
- @SuppressWarnings("unchecked")
@Test
public void headerContains() throws Exception {
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
@@ -99,13 +98,11 @@ public class MockRestRequestMatchersTests {
MockRestRequestMatchers.header("foo", containsString("ba")).match(this.request);
}
- @SuppressWarnings("unchecked")
@Test(expected = AssertionError.class)
public void headerContainsWithMissingHeader() throws Exception {
MockRestRequestMatchers.header("foo", containsString("baz")).match(this.request);
}
- @SuppressWarnings("unchecked")
@Test(expected = AssertionError.class)
public void headerContainsWithMissingValue() throws Exception {
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleAsyncTests.java b/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleAsyncTests.java
index 73b17cb5..091b64d5 100644
--- a/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleAsyncTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleAsyncTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.test.web.client.samples;
-import org.junit.Before;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
@@ -29,6 +29,7 @@ import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.client.AsyncRestTemplate;
import static org.junit.Assert.*;
+import static org.springframework.test.web.client.ExpectedCount.manyTimes;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
@@ -39,20 +40,14 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
* code.
*
* @author Rossen Stoyanchev
+ * @since 4.1
*/
public class SampleAsyncTests {
- private MockRestServiceServer mockServer;
-
- private AsyncRestTemplate restTemplate;
+ private final AsyncRestTemplate restTemplate = new AsyncRestTemplate();
+ private final MockRestServiceServer mockServer = MockRestServiceServer.createServer(this.restTemplate);
- @Before
- public void setup() {
- this.restTemplate = new AsyncRestTemplate();
- this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
-
- }
@Test
public void performGet() throws Exception {
@@ -63,7 +58,8 @@ public class SampleAsyncTests {
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
- ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+ ListenableFuture<ResponseEntity<Person>> ludwig =
+ this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
// We are only validating the request. The response is mocked out.
// person.getName().equals("Ludwig van Beethoven")
@@ -73,19 +69,26 @@ public class SampleAsyncTests {
}
@Test
- public void performGetAsync() throws Exception {
+ public void performGetManyTimes() throws Exception {
String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
- this.mockServer.expect(requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
+ this.mockServer.expect(manyTimes(), requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
- ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+ ListenableFuture<ResponseEntity<Person>> ludwig =
+ this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+ // We are only validating the request. The response is mocked out.
// person.getName().equals("Ludwig van Beethoven")
// person.getDouble().equals(1.6035)
+ this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+ this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+ this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+ this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+
this.mockServer.verify();
}
@@ -98,7 +101,8 @@ public class SampleAsyncTests {
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
- ListenableFuture<ResponseEntity<Person>> ludwig = restTemplate.getForEntity("/composers/{id}", Person.class, 42);
+ ListenableFuture<ResponseEntity<Person>> ludwig =
+ this.restTemplate.getForEntity("/composers/{id}", Person.class, 42);
// hotel.getId() == 42
// hotel.getName().equals("Holiday Inn")
@@ -132,7 +136,8 @@ public class SampleAsyncTests {
this.mockServer.verify();
}
catch (AssertionError error) {
- assertTrue(error.getMessage(), error.getMessage().contains("2 out of 4 were executed"));
+ assertTrue(error.getMessage(), error.getMessage().contains("2 unsatisfied expectation(s)"));
}
}
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleTests.java b/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleTests.java
index d3fc4823..6d5c7452 100644
--- a/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/client/samples/SampleTests.java
@@ -27,6 +27,7 @@ import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertTrue;
+import static org.springframework.test.web.client.ExpectedCount.manyTimes;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@@ -48,7 +49,7 @@ public class SampleTests {
@Before
public void setup() {
this.restTemplate = new RestTemplate();
- this.mockServer = MockRestServiceServer.createServer(this.restTemplate);
+ this.mockServer = MockRestServiceServer.bindTo(this.restTemplate).ignoreExpectOrder(true).build();
}
@Test
@@ -60,7 +61,7 @@ public class SampleTests {
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
- Person ludwig = restTemplate.getForObject("/composers/{id}", Person.class, 42);
+ Person ludwig = this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
// We are only validating the request. The response is mocked out.
// hotel.getId() == 42
@@ -70,6 +71,28 @@ public class SampleTests {
}
@Test
+ public void performGetManyTimes() throws Exception {
+
+ String responseBody = "{\"name\" : \"Ludwig van Beethoven\", \"someDouble\" : \"1.6035\"}";
+
+ this.mockServer.expect(manyTimes(), requestTo("/composers/42")).andExpect(method(HttpMethod.GET))
+ .andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
+
+ @SuppressWarnings("unused")
+ Person ludwig = this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
+
+ // We are only validating the request. The response is mocked out.
+ // hotel.getId() == 42
+ // hotel.getName().equals("Holiday Inn")
+
+ this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
+ this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
+ this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
+
+ this.mockServer.verify();
+ }
+
+ @Test
public void performGetWithResponseBodyFromFile() throws Exception {
Resource responseBody = new ClassPathResource("ludwig.json", this.getClass());
@@ -78,7 +101,7 @@ public class SampleTests {
.andRespond(withSuccess(responseBody, MediaType.APPLICATION_JSON));
@SuppressWarnings("unused")
- Person ludwig = restTemplate.getForObject("/composers/{id}", Person.class, 42);
+ Person ludwig = this.restTemplate.getForObject("/composers/{id}", Person.class, 42);
// hotel.getId() == 42
// hotel.getName().equals("Holiday Inn")
@@ -102,17 +125,18 @@ public class SampleTests {
.andRespond(withSuccess("8", MediaType.TEXT_PLAIN));
@SuppressWarnings("unused")
- String result = this.restTemplate.getForObject("/number", String.class);
- // result == "1"
+ String result1 = this.restTemplate.getForObject("/number", String.class);
+ // result1 == "1"
- result = this.restTemplate.getForObject("/number", String.class);
+ @SuppressWarnings("unused")
+ String result2 = this.restTemplate.getForObject("/number", String.class);
// result == "2"
try {
this.mockServer.verify();
}
catch (AssertionError error) {
- assertTrue(error.getMessage(), error.getMessage().contains("2 out of 4 were executed"));
+ assertTrue(error.getMessage(), error.getMessage().contains("2 unsatisfied expectation(s)"));
}
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatchersIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatchersIntegrationTests.java
index 7ffb5eb9..1b56ffa2 100644
--- a/spring-test/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatchersIntegrationTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/client/samples/matchers/HeaderRequestMatchersIntegrationTests.java
@@ -71,7 +71,6 @@ public class HeaderRequestMatchersIntegrationTests {
this.mockServer.verify();
}
- @SuppressWarnings("unchecked")
@Test
public void testStringContains() throws Exception {
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java
index beab7668..bf347eed 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java
@@ -129,8 +129,7 @@ public class DelegatingWebConnectionTests {
WebClient webClient = new WebClient();
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(TestController.class).build();
- MockMvcWebConnection mockConnection = new MockMvcWebConnection(mockMvc);
- mockConnection.setWebClient(webClient);
+ MockMvcWebConnection mockConnection = new MockMvcWebConnection(mockMvc, webClient);
WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
WebConnection httpConnection = new HttpWebConnection(webClient);
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java
index ddf68306..4c7f9cb5 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -16,7 +16,6 @@
package org.springframework.test.web.servlet.htmlunit;
-import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
@@ -28,11 +27,9 @@ import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
-import com.gargoylesoftware.htmlunit.HttpMethod;
-import com.gargoylesoftware.htmlunit.WebClient;
-import com.gargoylesoftware.htmlunit.WebRequest;
-import com.gargoylesoftware.htmlunit.util.NameValuePair;
+import org.apache.commons.io.IOUtils;
import org.apache.http.auth.UsernamePasswordCredentials;
+
import org.junit.Before;
import org.junit.Test;
@@ -42,12 +39,16 @@ import org.springframework.mock.web.MockServletContext;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.util.FileCopyUtils;
-import static java.util.Arrays.*;
+import com.gargoylesoftware.htmlunit.HttpMethod;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.WebRequest;
+import com.gargoylesoftware.htmlunit.util.NameValuePair;
+
+import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.junit.Assert.assertThat;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
/**
* Unit tests for {@link HtmlUnitRequestBuilder}.
@@ -76,6 +77,7 @@ public class HtmlUnitRequestBuilderTests {
requestBuilder = new HtmlUnitRequestBuilder(sessions, webClient, webRequest);
}
+ // --- constructor
@Test(expected = IllegalArgumentException.class)
public void constructorNullSessions() {
@@ -92,6 +94,8 @@ public class HtmlUnitRequestBuilderTests {
new HtmlUnitRequestBuilder(sessions, webClient, null);
}
+ // --- buildRequest
+
@Test
@SuppressWarnings("deprecation")
public void buildRequestBasicAuth() {
@@ -241,8 +245,7 @@ public class HtmlUnitRequestBuilderTests {
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
- assertThat(FileCopyUtils.copyToString(new InputStreamReader(actualRequest.getInputStream(), "ISO-8859-1")),
- equalTo(content));
+ assertThat(IOUtils.toString(actualRequest.getInputStream()), equalTo(content));
}
@Test
@@ -408,7 +411,8 @@ public class HtmlUnitRequestBuilderTests {
assertThat(actualRequest.getParameter("name"), equalTo("value"));
}
- @Test // SPR-14177
+ // SPR-14177
+ @Test
public void buildRequestParameterMapDecodesParameterName() throws Exception {
webRequest.setUrl(new URL("http://example.com/example/?row%5B0%5D=value"));
@@ -561,7 +565,7 @@ public class HtmlUnitRequestBuilderTests {
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
- assertThat(FileCopyUtils.copyToString(actualRequest.getReader()), equalTo(expectedBody));
+ assertThat(IOUtils.toString(actualRequest.getReader()), equalTo(expectedBody));
}
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java
index c89f4f40..303f5af9 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -20,6 +20,11 @@ import java.io.IOException;
import java.net.URL;
import javax.servlet.http.HttpServletRequest;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.WebConnection;
+import com.gargoylesoftware.htmlunit.WebRequest;
+import com.gargoylesoftware.htmlunit.WebResponse;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,20 +41,18 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import com.gargoylesoftware.htmlunit.WebConnection;
-import com.gargoylesoftware.htmlunit.WebRequest;
-import com.gargoylesoftware.htmlunit.WebResponse;
-
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* Integration tests for {@link MockMvcWebConnectionBuilderSupport}.
*
* @author Rob Winch
+ * @author Rossen Stoyanchev
* @since 4.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -58,93 +61,85 @@ import static org.mockito.Mockito.mock;
@SuppressWarnings("rawtypes")
public class MockMvcConnectionBuilderSupportTests {
- private final WebConnection delegateConnection = mock(WebConnection.class);
+ private final WebClient client = mock(WebClient.class);
+
+ private MockMvcWebConnectionBuilderSupport builder;
@Autowired
private WebApplicationContext wac;
- private MockMvc mockMvc;
-
- private WebConnection connection;
@Before
public void setup() {
- mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
-
- connection = new MockMvcWebConnectionBuilderSupport(mockMvc){}
- .createConnection(delegateConnection);
+ when(this.client.getWebConnection()).thenReturn(mock(WebConnection.class));
+ this.builder = new MockMvcWebConnectionBuilderSupport(this.wac) {};
}
+
@Test(expected = IllegalArgumentException.class)
public void constructorMockMvcNull() {
- new MockMvcWebConnectionBuilderSupport((MockMvc)null){};
+ new MockMvcWebConnectionBuilderSupport((MockMvc) null){};
}
@Test(expected = IllegalArgumentException.class)
public void constructorContextNull() {
- new MockMvcWebConnectionBuilderSupport((WebApplicationContext)null){};
+ new MockMvcWebConnectionBuilderSupport((WebApplicationContext) null){};
}
@Test
public void context() throws Exception {
- connection = new MockMvcWebConnectionBuilderSupport(wac) {}
- .createConnection(delegateConnection);
+ WebConnection conn = this.builder.createConnection(this.client);
- assertMvcProcessed("http://localhost/");
- assertDelegateProcessed("http://example.com/");
+ assertMockMvcUsed(conn, "http://localhost/");
+ assertMockMvcNotUsed(conn, "http://example.com/");
}
@Test
public void mockMvc() throws Exception {
- assertMvcProcessed("http://localhost/");
- assertDelegateProcessed("http://example.com/");
+ MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+ WebConnection conn = new MockMvcWebConnectionBuilderSupport(mockMvc) {}.createConnection(this.client);
+
+ assertMockMvcUsed(conn, "http://localhost/");
+ assertMockMvcNotUsed(conn, "http://example.com/");
}
@Test
public void mockMvcExampleDotCom() throws Exception {
- connection = new MockMvcWebConnectionBuilderSupport(wac) {}
- .useMockMvcForHosts("example.com")
- .createConnection(delegateConnection);
+ WebConnection conn = this.builder.useMockMvcForHosts("example.com").createConnection(this.client);
- assertMvcProcessed("http://localhost/");
- assertMvcProcessed("http://example.com/");
- assertDelegateProcessed("http://other.com/");
+ assertMockMvcUsed(conn, "http://localhost/");
+ assertMockMvcUsed(conn, "http://example.com/");
+ assertMockMvcNotUsed(conn, "http://other.com/");
}
@Test
public void mockMvcAlwaysUseMockMvc() throws Exception {
- connection = new MockMvcWebConnectionBuilderSupport(wac) {}
- .alwaysUseMockMvc()
- .createConnection(delegateConnection);
-
- assertMvcProcessed("http://other.com/");
+ WebConnection conn = this.builder.alwaysUseMockMvc().createConnection(this.client);
+ assertMockMvcUsed(conn, "http://other.com/");
}
@Test
public void defaultContextPathEmpty() throws Exception {
- connection = new MockMvcWebConnectionBuilderSupport(wac) {}
- .createConnection(delegateConnection);
-
- assertThat(getWebResponse("http://localhost/abc").getContentAsString(), equalTo(""));
+ WebConnection conn = this.builder.createConnection(this.client);
+ assertThat(getResponse(conn, "http://localhost/abc").getContentAsString(), equalTo(""));
}
@Test
public void defaultContextPathCustom() throws Exception {
- connection = new MockMvcWebConnectionBuilderSupport(wac) {}
- .contextPath("/abc").createConnection(delegateConnection);
-
- assertThat(getWebResponse("http://localhost/abc/def").getContentAsString(), equalTo("/abc"));
+ WebConnection conn = this.builder.contextPath("/abc").createConnection(this.client);
+ assertThat(getResponse(conn, "http://localhost/abc/def").getContentAsString(), equalTo("/abc"));
}
- private void assertMvcProcessed(String url) throws Exception {
- assertThat(getWebResponse(url), notNullValue());
+
+ private void assertMockMvcUsed(WebConnection connection, String url) throws Exception {
+ assertThat(getResponse(connection, url), notNullValue());
}
- private void assertDelegateProcessed(String url) throws Exception {
- assertThat(getWebResponse(url), nullValue());
+ private void assertMockMvcNotUsed(WebConnection connection, String url) throws Exception {
+ assertThat(getResponse(connection, url), nullValue());
}
- private WebResponse getWebResponse(String url) throws IOException {
+ private WebResponse getResponse(WebConnection connection, String url) throws IOException {
return connection.getResponse(new WebRequest(new URL(url)));
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java
index a7473c18..79a6af18 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java
@@ -19,10 +19,13 @@ package org.springframework.test.web.servlet.htmlunit;
import java.io.IOException;
import java.net.URL;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import com.gargoylesoftware.htmlunit.HttpMethod;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
+import com.gargoylesoftware.htmlunit.util.Cookie;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,13 +33,17 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.tests.Assume;
import org.springframework.tests.TestGroup;
+import org.springframework.web.bind.annotation.CookieValue;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -49,9 +56,10 @@ import static org.junit.Assert.*;
*
* @author Rob Winch
* @author Sam Brannen
+ * @author Rossen Stoyanchev
* @since 4.2
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class MockMvcWebClientBuilderTests {
@@ -61,8 +69,6 @@ public class MockMvcWebClientBuilderTests {
private MockMvc mockMvc;
- private WebClient webClient;
-
@Before
public void setup() {
@@ -82,31 +88,65 @@ public class MockMvcWebClientBuilderTests {
@Test
public void mockMvcSetupWithDefaultWebClientDelegate() throws Exception {
- this.webClient = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).build();
+ WebClient client = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).build();
- assertMvcProcessed("http://localhost/test");
- Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/"));
+ assertMockMvcUsed(client, "http://localhost/test");
+ Assume.group(TestGroup.PERFORMANCE, () -> assertMockMvcNotUsed(client, "http://example.com/"));
}
@Test
public void mockMvcSetupWithCustomWebClientDelegate() throws Exception {
WebClient otherClient = new WebClient();
- this.webClient = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).withDelegate(otherClient).build();
+ WebClient client = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).withDelegate(otherClient).build();
+
+ assertMockMvcUsed(client, "http://localhost/test");
+ Assume.group(TestGroup.PERFORMANCE, () -> assertMockMvcNotUsed(client, "http://example.com/"));
+ }
+
+ @Test // SPR-14066
+ public void cookieManagerShared() throws Exception {
+ this.mockMvc = MockMvcBuilders.standaloneSetup(new CookieController()).build();
+ WebClient client = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).build();
+
+ assertThat(getResponse(client, "http://localhost/").getContentAsString(), equalTo("NA"));
+ client.getCookieManager().addCookie(new Cookie("localhost", "cookie", "cookieManagerShared"));
+ assertThat(getResponse(client, "http://localhost/").getContentAsString(), equalTo("cookieManagerShared"));
+ }
+
+ @Test // SPR-14265
+ public void cookiesAreManaged() throws Exception {
+ this.mockMvc = MockMvcBuilders.standaloneSetup(new CookieController()).build();
+ WebClient client = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).build();
+
+ assertThat(getResponse(client, "http://localhost/").getContentAsString(), equalTo("NA"));
+ assertThat(postResponse(client, "http://localhost/?cookie=foo").getContentAsString(), equalTo("Set"));
+ assertThat(getResponse(client, "http://localhost/").getContentAsString(), equalTo("foo"));
+ assertThat(deleteResponse(client, "http://localhost/").getContentAsString(), equalTo("Delete"));
+ assertThat(getResponse(client, "http://localhost/").getContentAsString(), equalTo("NA"));
+ }
+
+ private void assertMockMvcUsed(WebClient client, String url) throws Exception {
+ assertThat(getResponse(client, url).getContentAsString(), equalTo("mvc"));
+ }
- assertMvcProcessed("http://localhost/test");
- Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/"));
+ private void assertMockMvcNotUsed(WebClient client, String url) throws Exception {
+ assertThat(getResponse(client, url).getContentAsString(), not(equalTo("mvc")));
}
- private void assertMvcProcessed(String url) throws Exception {
- assertThat(getWebResponse(url).getContentAsString(), equalTo("mvc"));
+ private WebResponse getResponse(WebClient client, String url) throws IOException {
+ return createResponse(client, new WebRequest(new URL(url)));
}
- private void assertDelegateProcessed(String url) throws Exception {
- assertThat(getWebResponse(url).getContentAsString(), not(equalTo("mvc")));
+ private WebResponse postResponse(WebClient client, String url) throws IOException {
+ return createResponse(client, new WebRequest(new URL(url), HttpMethod.POST));
}
- private WebResponse getWebResponse(String url) throws IOException {
- return this.webClient.getWebConnection().getResponse(new WebRequest(new URL(url)));
+ private WebResponse deleteResponse(WebClient client, String url) throws IOException {
+ return createResponse(client, new WebRequest(new URL(url), HttpMethod.DELETE));
+ }
+
+ private WebResponse createResponse(WebClient client, WebRequest request) throws IOException {
+ return client.getWebConnection().getResponse(request);
}
@@ -124,4 +164,29 @@ public class MockMvcWebClientBuilderTests {
}
}
+ @RestController
+ static class CookieController {
+
+ static final String COOKIE_NAME = "cookie";
+
+ @RequestMapping(path = "/", produces = "text/plain")
+ String cookie(@CookieValue(name = COOKIE_NAME, defaultValue = "NA") String cookie) {
+ return cookie;
+ }
+
+ @PostMapping(path = "/", produces = "text/plain")
+ String setCookie(@RequestParam String cookie, HttpServletResponse response) {
+ response.addCookie(new javax.servlet.http.Cookie(COOKIE_NAME, cookie));
+ return "Set";
+ }
+
+ @DeleteMapping(path = "/", produces = "text/plain")
+ String deleteCookie(HttpServletResponse response) {
+ javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie(COOKIE_NAME, "");
+ cookie.setMaxAge(0);
+ response.addCookie(cookie);
+ return "Delete";
+ }
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java
index b74a22f2..1b3ba8ce 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java
@@ -35,6 +35,7 @@ import static org.junit.Assert.*;
* @author Rob Winch
* @since 4.2
*/
+@SuppressWarnings("deprecation")
public class MockMvcWebConnectionTests {
private final WebClient webClient = new WebClient();
@@ -49,7 +50,7 @@ public class MockMvcWebConnectionTests {
@Test
public void contextPathNull() throws IOException {
- this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, null));
+ this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, (String) null));
Page page = this.webClient.getPage("http://localhost/context/a");
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java
index f367153c..29c83115 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java
@@ -18,6 +18,7 @@ package org.springframework.test.web.servlet.htmlunit;
import java.net.URL;
import java.util.List;
+import javax.servlet.http.Cookie;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
@@ -95,16 +96,41 @@ public class MockWebResponseBuilderTests {
public void buildResponseHeaders() throws Exception {
this.response.addHeader("Content-Type", "text/html");
this.response.addHeader("X-Test", "value");
+ Cookie cookie = new Cookie("cookieA", "valueA");
+ cookie.setDomain("domain");
+ cookie.setPath("/path");
+ cookie.setMaxAge(1800);
+ cookie.setSecure(true);
+ cookie.setHttpOnly(true);
+ this.response.addCookie(cookie);
WebResponse webResponse = this.responseBuilder.build();
List<NameValuePair> responseHeaders = webResponse.getResponseHeaders();
- assertThat(responseHeaders.size(), equalTo(2));
+ assertThat(responseHeaders.size(), equalTo(3));
NameValuePair header = responseHeaders.get(0);
assertThat(header.getName(), equalTo("Content-Type"));
assertThat(header.getValue(), equalTo("text/html"));
header = responseHeaders.get(1);
assertThat(header.getName(), equalTo("X-Test"));
assertThat(header.getValue(), equalTo("value"));
+ header = responseHeaders.get(2);
+ assertThat(header.getName(), equalTo("Set-Cookie"));
+ assertThat(header.getValue(), startsWith("cookieA=valueA;domain=domain;path=/path;expires="));
+ assertThat(header.getValue(), endsWith(";secure;httpOnly"));
+ }
+
+ // SPR-14169
+ @Test
+ public void buildResponseHeadersNullDomainDefaulted() throws Exception {
+ Cookie cookie = new Cookie("cookieA", "valueA");
+ this.response.addCookie(cookie);
+ WebResponse webResponse = this.responseBuilder.build();
+
+ List<NameValuePair> responseHeaders = webResponse.getResponseHeaders();
+ assertThat(responseHeaders.size(), equalTo(1));
+ NameValuePair header = responseHeaders.get(0);
+ assertThat(header.getName(), equalTo("Set-Cookie"));
+ assertThat(header.getValue(), equalTo("cookieA=valueA"));
}
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java
index 85e41b02..f60e1a76 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java
@@ -19,6 +19,7 @@ package org.springframework.test.web.servlet.htmlunit.webdriver;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
+import com.gargoylesoftware.htmlunit.util.Cookie;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,12 +28,13 @@ import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.tests.Assume;
import org.springframework.tests.TestGroup;
+import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
@@ -48,7 +50,7 @@ import static org.junit.Assert.*;
* @author Sam Brannen
* @since 4.2
*/
-@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class MockMvcHtmlUnitDriverBuilderTests {
@@ -84,16 +86,16 @@ public class MockMvcHtmlUnitDriverBuilderTests {
WebConnectionHtmlUnitDriver otherDriver = new WebConnectionHtmlUnitDriver();
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).withDelegate(otherDriver).build();
- assertMvcProcessed("http://localhost/test");
- Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/"));
+ assertMockMvcUsed("http://localhost/test");
+ Assume.group(TestGroup.PERFORMANCE, () -> assertMockMvcNotUsed("http://example.com/"));
}
@Test
public void mockMvcSetupWithDefaultDriverDelegate() throws Exception {
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
- assertMvcProcessed("http://localhost/test");
- Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/"));
+ assertMockMvcUsed("http://localhost/test");
+ Assume.group(TestGroup.PERFORMANCE, () -> assertMockMvcNotUsed("http://example.com/"));
}
@Test
@@ -108,11 +110,25 @@ public class MockMvcHtmlUnitDriverBuilderTests {
assertFalse(this.driver.isJavascriptEnabled());
}
- private void assertMvcProcessed(String url) throws Exception {
+ @Test // SPR-14066
+ public void cookieManagerShared() throws Exception {
+ WebConnectionHtmlUnitDriver otherDriver = new WebConnectionHtmlUnitDriver();
+ this.mockMvc = MockMvcBuilders.standaloneSetup(new CookieController()).build();
+ this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc)
+ .withDelegate(otherDriver).build();
+
+ assertThat(get("http://localhost/"), equalTo(""));
+ Cookie cookie = new Cookie("localhost", "cookie", "cookieManagerShared");
+ otherDriver.getWebClient().getCookieManager().addCookie(cookie);
+ assertThat(get("http://localhost/"), equalTo("cookieManagerShared"));
+ }
+
+
+ private void assertMockMvcUsed(String url) throws Exception {
assertThat(get(url), containsString(EXPECTED_BODY));
}
- private void assertDelegateProcessed(String url) throws Exception {
+ private void assertMockMvcNotUsed(String url) throws Exception {
assertThat(get(url), not(containsString(EXPECTED_BODY)));
}
@@ -136,4 +152,13 @@ public class MockMvcHtmlUnitDriverBuilderTests {
}
}
-} \ No newline at end of file
+ @RestController
+ static class CookieController {
+
+ @RequestMapping(path = "/", produces = "text/plain")
+ String cookie(@CookieValue("cookie") String cookie) {
+ return cookie;
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java
index 90ab35ba..53c76989 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -18,6 +18,7 @@ package org.springframework.test.web.servlet.request;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.Charset;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collections;
@@ -25,7 +26,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
@@ -43,8 +43,12 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.support.SessionFlashMapManager;
+import org.springframework.web.util.UriComponentsBuilder;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
/**
* Unit tests for building a {@link MockHttpServletRequest} with
@@ -270,6 +274,20 @@ public class MockHttpServletRequestBuilderTests {
}
@Test
+ public void requestParameterFromRequestBodyFormData() throws Exception {
+ String contentType = "application/x-www-form-urlencoded;charset=UTF-8";
+ String body = "name+1=value+1&name+2=value+A&name+2=value+B&name+3";
+
+ MockHttpServletRequest request = new MockHttpServletRequestBuilder(HttpMethod.POST, "/foo")
+ .contentType(contentType).content(body.getBytes(Charset.forName("UTF-8")))
+ .buildRequest(this.servletContext);
+
+ assertArrayEquals(new String[] {"value 1"}, request.getParameterMap().get("name 1"));
+ assertArrayEquals(new String[] {"value A", "value B"}, request.getParameterMap().get("name 2"));
+ assertArrayEquals(new String[] {null}, request.getParameterMap().get("name 3"));
+ }
+
+ @Test
public void acceptHeader() {
this.builder.accept(MediaType.TEXT_HTML, MediaType.APPLICATION_XML);
@@ -429,7 +447,7 @@ public class MockHttpServletRequestBuilderTests {
@Test
public void sessionAttributes() {
- Map<String, Object> map = new HashMap<String, Object>();
+ Map<String, Object> map = new HashMap<>();
map.put("foo", "bar");
this.builder.sessionAttrs(map);
@@ -472,6 +490,7 @@ public class MockHttpServletRequestBuilderTests {
}
// SPR-12945
+
@Test
public void mergeInvokesDefaultRequestPostProcessorFirst() {
final String ATTR = "ATTR";
@@ -492,6 +511,20 @@ public class MockHttpServletRequestBuilderTests {
assertEquals(EXEPCTED, request.getAttribute(ATTR));
}
+ // SPR-13719
+
+ @Test
+ public void arbitraryMethod() {
+ String httpMethod = "REPort";
+ URI url = UriComponentsBuilder.fromPath("/foo/{bar}").buildAndExpand(42).toUri();
+ this.builder = new MockHttpServletRequestBuilder(httpMethod, url);
+
+ MockHttpServletRequest request = this.builder.buildRequest(this.servletContext);
+
+ assertEquals(httpMethod, request.getMethod());
+ assertEquals("/foo/42", request.getPathInfo());
+ }
+
private final class User implements Principal {
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/result/JsonPathResultMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/result/JsonPathResultMatchersTests.java
index bf0b5885..b587a34e 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/result/JsonPathResultMatchersTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/result/JsonPathResultMatchersTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -29,6 +29,7 @@ import org.springframework.test.web.servlet.StubMvcResult;
* @author Rossen Stoyanchev
* @author Craig Andrews
* @author Sam Brannen
+ * @author Brian Clozel
*/
public class JsonPathResultMatchersTests {
@@ -41,7 +42,7 @@ public class JsonPathResultMatchersTests {
"'emptyString': '', " + //
"'emptyArray': [], " + //
"'emptyMap': {} " + //
- "}";
+ "}";
private static final StubMvcResult stubMvcResult;
@@ -57,7 +58,6 @@ public class JsonPathResultMatchersTests {
}
}
-
@Test
public void value() throws Exception {
new JsonPathResultMatchers("$.str").value("foo").match(stubMvcResult);
@@ -233,4 +233,42 @@ public class JsonPathResultMatchersTests {
new JsonPathResultMatchers("$.arr").isString().match(stubMvcResult);
}
+ @Test(expected = AssertionError.class)
+ public void valueWithJsonPrefixNotConfigured() throws Exception {
+ String jsonPrefix = "prefix";
+ StubMvcResult result = createPrefixedStubMvcResult(jsonPrefix);
+ new JsonPathResultMatchers("$.str").value("foo").match(result);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void valueWithJsonWrongPrefix() throws Exception {
+ String jsonPrefix = "prefix";
+ StubMvcResult result = createPrefixedStubMvcResult(jsonPrefix);
+ new JsonPathResultMatchers("$.str").prefix("wrong").value("foo").match(result);
+ }
+
+ @Test
+ public void valueWithJsonPrefix() throws Exception {
+ String jsonPrefix = "prefix";
+ StubMvcResult result = createPrefixedStubMvcResult(jsonPrefix);
+ new JsonPathResultMatchers("$.str").prefix(jsonPrefix).value("foo").match(result);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void prefixWithPayloadNotLongEnough() throws Exception {
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ response.addHeader("Content-Type", "application/json");
+ response.getWriter().print(new String("test".getBytes("ISO-8859-1")));
+ StubMvcResult result = new StubMvcResult(null, null, null, null, null, null, response);
+
+ new JsonPathResultMatchers("$.str").prefix("prefix").value("foo").match(result);
+ }
+
+ private StubMvcResult createPrefixedStubMvcResult(String jsonPrefix) throws Exception {
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ response.addHeader("Content-Type", "application/json");
+ response.getWriter().print(jsonPrefix + new String(RESPONSE_CONTENT.getBytes("ISO-8859-1")));
+ return new StubMvcResult(null, null, null, null, null, null, response);
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/result/StatusResultMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/result/StatusResultMatchersTests.java
index 7b1c04d4..ead30b9e 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/result/StatusResultMatchersTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/result/StatusResultMatchersTests.java
@@ -59,7 +59,7 @@ public class StatusResultMatchersTests {
List<AssertionError> failures = new ArrayList<AssertionError>();
- for(HttpStatus status : HttpStatus.values()) {
+ for (HttpStatus status : HttpStatus.values()) {
MockHttpServletResponse response = new MockHttpServletResponse();
response.setStatus(status.value());
MvcResult mvcResult = new StubMvcResult(request, null, null, null, null, null, response);
@@ -91,9 +91,7 @@ public class StatusResultMatchersTests {
@Test
public void statusRanges() throws Exception {
-
- for(HttpStatus status : HttpStatus.values()) {
-
+ for (HttpStatus status : HttpStatus.values()) {
MockHttpServletResponse response = new MockHttpServletResponse();
response.setStatus(status.value());
MvcResult mvcResult = new StubMvcResult(request, null, null, null, null, null, response);
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/AsyncControllerJavaConfigTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/AsyncControllerJavaConfigTests.java
index 31943512..0613048d 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/AsyncControllerJavaConfigTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/AsyncControllerJavaConfigTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -23,6 +23,7 @@ import java.util.concurrent.Callable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
@@ -36,7 +37,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
@@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* Tests with Java configuration.
*
* @author Rossen Stoyanchev
+ * @author Sam Brannen
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@@ -123,7 +125,7 @@ public class AsyncControllerJavaConfigTests {
@RestController
static class AsyncController {
- @RequestMapping(path = "/callable")
+ @GetMapping("/callable")
public Callable<Map<String, String>> getCallable() {
return () -> Collections.singletonMap("key", "value");
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/PersonController.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/PersonController.java
index 5e115f66..ee365936 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/PersonController.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/context/PersonController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,25 +16,24 @@
package org.springframework.test.web.servlet.samples.context;
-import org.springframework.stereotype.Controller;
import org.springframework.test.web.Person;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
-@Controller
+@RestController
+@RequestMapping("/person")
public class PersonController {
private final PersonDao personDao;
- public PersonController(PersonDao personDao) {
+ PersonController(PersonDao personDao) {
this.personDao = personDao;
}
- @RequestMapping(value="/person/{id}", method=RequestMethod.GET)
- @ResponseBody
+ @GetMapping("/{id}")
public Person getPerson(@PathVariable long id) {
return this.personDao.getPerson(id);
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java
index 48038763..c922297f 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -17,6 +17,7 @@
package org.springframework.test.web.servlet.samples.standalone;
import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
@@ -26,6 +27,7 @@ import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
import org.springframework.test.web.Person;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
@@ -36,6 +38,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
+import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@@ -67,11 +70,39 @@ public class AsyncTests {
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
}
@Test
+ public void streaming() throws Exception {
+ this.mockMvc.perform(get("/1").param("streaming", "true"))
+ .andExpect(request().asyncStarted())
+ .andDo(r -> r.getAsyncResult()) // fetch async result similar to "asyncDispatch" builder
+ .andExpect(status().isOk())
+ .andExpect(content().string("name=Joe"));
+ }
+
+ @Test
+ public void streamingSlow() throws Exception {
+ this.mockMvc.perform(get("/1").param("streamingSlow", "true"))
+ .andExpect(request().asyncStarted())
+ .andDo(r -> r.getAsyncResult())
+ .andExpect(status().isOk())
+ .andExpect(content().string("name=Joe&someBoolean=true"));
+ }
+
+ @Test
+ public void streamingJson() throws Exception {
+ this.mockMvc.perform(get("/1").param("streamingJson", "true"))
+ .andExpect(request().asyncStarted())
+ .andDo(r -> r.getAsyncResult())
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.5}"));
+ }
+
+ @Test
public void deferredResult() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/1").param("deferredResult", "true"))
.andExpect(request().asyncStarted())
@@ -81,7 +112,7 @@ public class AsyncTests {
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
}
@@ -94,7 +125,7 @@ public class AsyncTests {
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
}
@@ -122,7 +153,7 @@ public class AsyncTests {
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
}
@@ -137,7 +168,7 @@ public class AsyncTests {
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
}
@@ -161,7 +192,7 @@ public class AsyncTests {
this.mockMvc.perform(asyncDispatch(mvcResult))
.andDo(print(writer))
.andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
assertTrue(writer.toString().contains("Async started = false"));
@@ -184,6 +215,31 @@ public class AsyncTests {
return () -> new Person("Joe");
}
+ @RequestMapping(params = "streaming")
+ public StreamingResponseBody getStreaming() {
+ return os -> os.write("name=Joe".getBytes());
+ }
+
+ @RequestMapping(params = "streamingSlow")
+ public StreamingResponseBody getStreamingSlow() {
+ return os -> {
+ os.write("name=Joe".getBytes());
+ try {
+ Thread.sleep(200);
+ os.write("&someBoolean=true".getBytes());
+ }
+ catch (InterruptedException e) {
+ /* no-op */
+ }
+ };
+ }
+
+ @RequestMapping(params = "streamingJson")
+ public ResponseEntity<StreamingResponseBody> getStreamingJson() {
+ return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
+ .body(os -> os.write("{\"name\":\"Joe\",\"someDouble\":0.5}".getBytes(StandardCharsets.UTF_8)));
+ }
+
@RequestMapping(params = "deferredResult")
public DeferredResult<Person> getDeferredResult() {
DeferredResult<Person> deferredResult = new DeferredResult<Person>();
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ViewResolutionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ViewResolutionTests.java
index 0ac2b07c..b286f626 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ViewResolutionTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ViewResolutionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -53,10 +53,7 @@ public class ViewResolutionTests {
@Test
public void testJspOnly() throws Exception {
-
- InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
- viewResolver.setPrefix("/WEB-INF/");
- viewResolver.setSuffix(".jsp");
+ InternalResourceViewResolver viewResolver = new InternalResourceViewResolver("/WEB-INF/", ".jsp");
standaloneSetup(new PersonController()).setViewResolvers(viewResolver).build()
.perform(get("/person/Corea"))
@@ -68,7 +65,6 @@ public class ViewResolutionTests {
@Test
public void testJsonOnly() throws Exception {
-
standaloneSetup(new PersonController()).setSingleView(new MappingJackson2JsonView()).build()
.perform(get("/person/Corea"))
.andExpect(status().isOk())
@@ -78,7 +74,6 @@ public class ViewResolutionTests {
@Test
public void testXmlOnly() throws Exception {
-
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Person.class);
@@ -91,7 +86,6 @@ public class ViewResolutionTests {
@Test
public void testContentNegotiation() throws Exception {
-
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Person.class);
@@ -131,7 +125,6 @@ public class ViewResolutionTests {
@Test
public void defaultViewResolver() throws Exception {
-
standaloneSetup(new PersonController()).build()
.perform(get("/person/Corea"))
.andExpect(model().attribute("person", hasProperty("name", equalTo("Corea"))))
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java
index 0c470ea5..d0d60138 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java
@@ -16,8 +16,6 @@
package org.springframework.test.web.servlet.samples.standalone.resultmatchers;
-import java.nio.charset.Charset;
-
import org.junit.Before;
import org.junit.Test;
@@ -44,8 +42,6 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
*/
public class ContentAssertionTests {
- public static final MediaType TEXT_PLAIN_UTF8 = new MediaType("text", "plain", Charset.forName("UTF-8"));
-
private MockMvc mockMvc;
@Before
@@ -56,8 +52,10 @@ public class ContentAssertionTests {
@Test
public void testContentType() throws Exception {
this.mockMvc.perform(get("/handle").accept(MediaType.TEXT_PLAIN))
- .andExpect(content().contentType(MediaType.TEXT_PLAIN))
- .andExpect(content().contentType("text/plain"));
+ .andExpect(content().contentType(MediaType.valueOf("text/plain;charset=ISO-8859-1")))
+ .andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
+ .andExpect(content().contentTypeCompatibleWith("text/plain"))
+ .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN));
this.mockMvc.perform(get("/handleUtf8"))
.andExpect(content().contentType(MediaType.valueOf("text/plain;charset=UTF-8")))
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HandlerAssertionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HandlerAssertionTests.java
index 9d3b883d..2c671bbf 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HandlerAssertionTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HandlerAssertionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -18,66 +18,84 @@ package org.springframework.test.web.servlet.samples.standalone.resultmatchers;
import java.lang.reflect.Method;
-import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
-import org.springframework.stereotype.Controller;
+import org.springframework.http.ResponseEntity;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.RequestMapping;
-
-import static org.hamcrest.Matchers.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.handler;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
+import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on;
/**
- * Examples of expectations on the handler or handler method that executed the request.
- *
- * <p>Note that in most cases "handler" is synonymous with "controller".
- * For example an {@code @Controller} is a kind of handler.
+ * Examples of expectations on the controller type and controller method.
*
* @author Rossen Stoyanchev
+ * @author Sam Brannen
*/
public class HandlerAssertionTests {
- private MockMvc mockMvc;
+ private final MockMvc mockMvc = standaloneSetup(new SimpleController()).alwaysExpect(status().isOk()).build();
+
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
- @Before
- public void setup() {
- this.mockMvc = standaloneSetup(new SimpleController()).alwaysExpect(status().isOk()).build();
- }
@Test
- public void testHandlerType() throws Exception {
+ public void handlerType() throws Exception {
this.mockMvc.perform(get("/")).andExpect(handler().handlerType(SimpleController.class));
}
@Test
- public void testHandlerMethodNameEqualTo() throws Exception {
- this.mockMvc.perform(get("/")).andExpect(handler().methodName("handle"));
+ public void methodCallOnNonMock() throws Exception {
+ exception.expect(AssertionError.class);
+ exception.expectMessage("The supplied object [bogus] is not an instance of");
+ exception.expectMessage(MvcUriComponentsBuilder.MethodInvocationInfo.class.getName());
+ exception.expectMessage("Ensure that you invoke the handler method via MvcUriComponentsBuilder.on()");
- // Hamcrest matcher..
- this.mockMvc.perform(get("/")).andExpect(handler().methodName(equalTo("handle")));
+ this.mockMvc.perform(get("/")).andExpect(handler().methodCall("bogus"));
}
@Test
- public void testHandlerMethodNameMatcher() throws Exception {
+ public void methodCall() throws Exception {
+ this.mockMvc.perform(get("/")).andExpect(handler().methodCall(on(SimpleController.class).handle()));
+ }
+
+ @Test
+ public void methodName() throws Exception {
+ this.mockMvc.perform(get("/")).andExpect(handler().methodName("handle"));
+ }
+
+ @Test
+ public void methodNameMatchers() throws Exception {
+ this.mockMvc.perform(get("/")).andExpect(handler().methodName(equalTo("handle")));
this.mockMvc.perform(get("/")).andExpect(handler().methodName(is(not("save"))));
}
@Test
- public void testHandlerMethod() throws Exception {
+ public void method() throws Exception {
Method method = SimpleController.class.getMethod("handle");
this.mockMvc.perform(get("/")).andExpect(handler().method(method));
}
- @Controller
- private static class SimpleController {
+ @RestController
+ static class SimpleController {
@RequestMapping("/")
- public String handle() {
- return "view";
+ public ResponseEntity<Void> handle() {
+ return ResponseEntity.ok().build();
}
}
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java
index e81824c8..38fbba01 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,6 +16,11 @@
package org.springframework.test.web.servlet.samples.standalone.resultmatchers;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
import org.junit.Before;
import org.junit.Test;
@@ -28,16 +33,22 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.springframework.http.HttpHeaders.IF_MODIFIED_SINCE;
+import static org.springframework.http.HttpHeaders.LAST_MODIFIED;
+import static org.springframework.http.HttpHeaders.VARY;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
/**
* Examples of expectations on response header values.
@@ -48,86 +59,95 @@ import java.util.TimeZone;
*/
public class HeaderAssertionTests {
- private static final String EXPECTED_ASSERTION_ERROR_MSG = "Should have thrown an AssertionError";
-
- private static final String IF_MODIFIED_SINCE = "If-Modified-Since";
+ private static final String ERROR_MESSAGE = "Should have thrown an AssertionError";
- private static final String LAST_MODIFIED = "Last-Modified";
-
- private final long currentTime = System.currentTimeMillis();
private String now;
- private String oneMinuteAgo;
+ private String minuteAgo;
- private String oneSecondLater;
+ private String secondLater;
private MockMvc mockMvc;
- private PersonController personController;
+ private final long currentTime = System.currentTimeMillis();
@Before
public void setup() {
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
- this.now = dateFormat.format(new Date(currentTime));
- this.oneMinuteAgo = dateFormat.format(new Date(currentTime - (1000 * 60)));
- this.oneSecondLater = dateFormat.format(new Date(currentTime + 1000));
- this.personController = new PersonController();
- this.personController.setStubTimestamp(currentTime);
- this.mockMvc = standaloneSetup(this.personController).build();
+ this.now = dateFormat.format(new Date(this.currentTime));
+ this.minuteAgo = dateFormat.format(new Date(this.currentTime - (1000 * 60)));
+ this.secondLater = dateFormat.format(new Date(this.currentTime + 1000));
+
+ PersonController controller = new PersonController();
+ controller.setStubTimestamp(this.currentTime);
+ this.mockMvc = standaloneSetup(controller).build();
}
+
@Test
public void stringWithCorrectResponseHeaderValue() throws Exception {
- this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
- .andExpect(header().string(LAST_MODIFIED, now));
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, minuteAgo))
+ .andExpect(header().string(LAST_MODIFIED, now));
}
@Test
public void stringWithMatcherAndCorrectResponseHeaderValue() throws Exception {
- this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
- .andExpect(header().string(LAST_MODIFIED, equalTo(now)));
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, minuteAgo))
+ .andExpect(header().string(LAST_MODIFIED, equalTo(now)));
+ }
+
+ @Test
+ public void multiStringHeaderValue() throws Exception {
+ this.mockMvc.perform(get("/persons/1")).andExpect(header().stringValues(VARY, "foo", "bar"));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void multiStringHeaderValueWithMatchers() throws Exception {
+ this.mockMvc.perform(get("/persons/1"))
+ .andExpect(header().stringValues(VARY, hasItems(containsString("foo"), startsWith("bar"))));
}
@Test
public void dateValueWithCorrectResponseHeaderValue() throws Exception {
- this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
- .andExpect(header().dateValue(LAST_MODIFIED, currentTime));
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, minuteAgo))
+ .andExpect(header().dateValue(LAST_MODIFIED, this.currentTime));
}
@Test
public void longValueWithCorrectResponseHeaderValue() throws Exception {
- this.mockMvc.perform(get("/persons/1"))//
- .andExpect(header().longValue("X-Rate-Limiting", 42));
+ this.mockMvc.perform(get("/persons/1"))
+ .andExpect(header().longValue("X-Rate-Limiting", 42));
}
@Test
public void stringWithMissingResponseHeader() throws Exception {
- this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))//
- .andExpect(status().isNotModified())//
- .andExpect(header().string("X-Custom-Header", (String) null));
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))
+ .andExpect(status().isNotModified())
+ .andExpect(header().stringValues("X-Custom-Header"));
}
@Test
public void stringWithMatcherAndMissingResponseHeader() throws Exception {
- this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))//
- .andExpect(status().isNotModified())//
- .andExpect(header().string("X-Custom-Header", nullValue()));
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))
+ .andExpect(status().isNotModified())
+ .andExpect(header().string("X-Custom-Header", nullValue()));
}
@Test
public void longValueWithMissingResponseHeader() throws Exception {
try {
- this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))//
- .andExpect(status().isNotModified())//
- .andExpect(header().longValue("X-Custom-Header", 99L));
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))
+ .andExpect(status().isNotModified())
+ .andExpect(header().longValue("X-Custom-Header", 99L));
- fail(EXPECTED_ASSERTION_ERROR_MSG);
+ fail(ERROR_MESSAGE);
}
catch (AssertionError e) {
- if (EXPECTED_ASSERTION_ERROR_MSG.equals(e.getMessage())) {
+ if (ERROR_MESSAGE.equals(e.getMessage())) {
throw e;
}
assertEquals("Response does not contain header " + "X-Custom-Header", e.getMessage());
@@ -138,32 +158,30 @@ public class HeaderAssertionTests {
@Test
public void doesNotExist() throws Exception {
- this.mockMvc.perform(get("/persons/1"))
- .andExpect(header().doesNotExist("X-Custom-Header"));
+ this.mockMvc.perform(get("/persons/1")).andExpect(header().doesNotExist("X-Custom-Header"));
}
// SPR-10771
@Test(expected = AssertionError.class)
public void doesNotExistFail() throws Exception {
- this.mockMvc.perform(get("/persons/1"))
- .andExpect(header().doesNotExist(LAST_MODIFIED));
+ this.mockMvc.perform(get("/persons/1")).andExpect(header().doesNotExist(LAST_MODIFIED));
}
@Test
public void stringWithIncorrectResponseHeaderValue() throws Exception {
- assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, oneSecondLater), oneSecondLater);
+ assertIncorrectResponseHeader(header().string(LAST_MODIFIED, secondLater), secondLater);
}
@Test
public void stringWithMatcherAndIncorrectResponseHeaderValue() throws Exception {
- assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, equalTo(oneSecondLater)), oneSecondLater);
+ assertIncorrectResponseHeader(header().string(LAST_MODIFIED, equalTo(secondLater)), secondLater);
}
@Test
public void dateValueWithIncorrectResponseHeaderValue() throws Exception {
- long unexpected = currentTime + 1000;
- assertIncorrectResponseHeaderValue(header().dateValue(LAST_MODIFIED, unexpected), oneSecondLater);
+ long unexpected = this.currentTime + 1000;
+ assertIncorrectResponseHeader(header().dateValue(LAST_MODIFIED, unexpected), secondLater);
}
@Test(expected = AssertionError.class)
@@ -171,21 +189,20 @@ public class HeaderAssertionTests {
this.mockMvc.perform(get("/persons/1")).andExpect(header().longValue("X-Rate-Limiting", 1));
}
- private void assertIncorrectResponseHeaderValue(ResultMatcher resultMatcher, String unexpected) throws Exception {
+ private void assertIncorrectResponseHeader(ResultMatcher matcher, String unexpected) throws Exception {
try {
- this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
- .andExpect(resultMatcher);
+ this.mockMvc.perform(get("/persons/1")
+ .header(IF_MODIFIED_SINCE, minuteAgo))
+ .andExpect(matcher);
- fail(EXPECTED_ASSERTION_ERROR_MSG);
+ fail(ERROR_MESSAGE);
}
catch (AssertionError e) {
- if (EXPECTED_ASSERTION_ERROR_MSG.equals(e.getMessage())) {
+ if (ERROR_MESSAGE.equals(e.getMessage())) {
throw e;
}
- // [SPR-10659] Ensure that the header name is included in the message
- //
- // We don't use assertEquals() since we cannot control the formatting
- // produced by JUnit or Hamcrest.
+ // SPR-10659: ensure header name is in the message
+ // Unfortunately, we can't control formatting from JUnit or Hamcrest.
assertMessageContains(e, "Response header " + LAST_MODIFIED);
assertMessageContains(e, unexpected);
assertMessageContains(e, now);
@@ -198,8 +215,6 @@ public class HeaderAssertionTests {
}
- // -------------------------------------------------------------------------
-
@Controller
private static class PersonController {
@@ -216,6 +231,7 @@ public class HeaderAssertionTests {
.ok()
.lastModified(calculateLastModified(id))
.header("X-Rate-Limiting", "42")
+ .header("Vary", "foo", "bar")
.body(new Person("Jason"));
}
diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java
index 4ae88e7a..c1e25791 100644
--- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java
@@ -158,7 +158,7 @@ public class XpathAssertionTests {
standaloneSetup(new BlogFeedController()).build()
.perform(get("/blog.atom").accept(MediaType.APPLICATION_ATOM_XML))
.andExpect(status().isOk())
- .andExpect(content().contentType(MediaType.APPLICATION_ATOM_XML))
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_ATOM_XML))
.andExpect(xpath("//feed/title").string("Test Feed"))
.andExpect(xpath("//feed/icon").string("http://www.example.com/favicon.ico"));
}
diff --git a/spring-test/src/test/resources/log4j.properties b/spring-test/src/test/resources/log4j.properties
index 8b88c2e0..258e7b84 100644
--- a/spring-test/src/test/resources/log4j.properties
+++ b/spring-test/src/test/resources/log4j.properties
@@ -25,6 +25,9 @@ log4j.logger.org.springframework.test.context.web=WARN
#log4j.logger.org.springframework.test.context.support.AbstractGenericContextLoader=INFO
#log4j.logger.org.springframework.test.context.support.AnnotationConfigContextLoader=INFO
+# The following must be kept at DEBUG in order to test SPR-14363.
+log4j.logger.org.springframework.test.util=DEBUG
+
log4j.logger.org.springframework.test.web.servlet.result=DEBUG
#log4j.logger.org.springframework.test=TRACE