From f5d2b88e3f86d9475acf4f07941690739da535f5 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 25 Jan 2017 12:32:07 -0500 Subject: [PATCH] "sharedHttpSession" shortcut for MockMvc builders Issue: SPR-13820 --- .../setup/ConfigurableMockMvcBuilder.java | 7 ++ .../setup/SharedHttpSessionConfigurer.java | 69 +++++++++++ .../servlet/setup/SharedHttpSessionTests.java | 108 ++++++++++++++++++ src/asciidoc/testing.adoc | 44 ++++++- 4 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 spring-test/src/main/java/org/springframework/test/web/servlet/setup/SharedHttpSessionConfigurer.java create mode 100644 spring-test/src/test/java/org/springframework/test/web/servlet/setup/SharedHttpSessionTests.java diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java index b97d63969b..908dec126d 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java @@ -122,6 +122,13 @@ public interface ConfigurableMockMvcBuilderThere is a built-in {@link SharedHttpSessionConfigurer} that can be + * used to re-use the HTTP session across requests. 3rd party frameworks + * like Spring Security also use this mechanism to provide configuration + * shortcuts. + * + * @see SharedHttpSessionConfigurer */ T apply(MockMvcConfigurer configurer); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/SharedHttpSessionConfigurer.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/SharedHttpSessionConfigurer.java new file mode 100644 index 0000000000..8aedd8a021 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/SharedHttpSessionConfigurer.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.test.web.servlet.setup; + +import javax.servlet.http.HttpSession; + +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.web.context.WebApplicationContext; + +/** + * {@link MockMvcConfigurer} that stores and re-uses the HTTP session across + * multiple requests performed through the same {@code MockMvc} instance. + * + *

Example use: + *

+ * import static org.springframework.test.web.servlet.setup.SharedHttpSessionConfigurer.sharedHttpSession;
+ *
+ * // ...
+ *
+ * MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
+ *         .apply(sharedHttpSession())
+ *         .build();
+ *
+ * // Use mockMvc to perform requests ...
+ * 
+ * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public class SharedHttpSessionConfigurer implements MockMvcConfigurer { + + private HttpSession session; + + + @Override + public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { + builder.alwaysDo(result -> this.session = result.getRequest().getSession(false)); + } + + @Override + public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, + WebApplicationContext context) { + + return request -> { + if (this.session != null) { + request.setSession(this.session); + } + return request; + }; + } + + public static SharedHttpSessionConfigurer sharedHttpSession() { + return new SharedHttpSessionConfigurer(); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/SharedHttpSessionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/SharedHttpSessionTests.java new file mode 100644 index 0000000000..e079db97e2 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/SharedHttpSessionTests.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.test.web.servlet.setup; + +import javax.servlet.http.HttpSession; + +import org.junit.Test; + +import org.springframework.stereotype.Controller; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.web.bind.annotation.GetMapping; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.SharedHttpSessionConfigurer.sharedHttpSession; + +/** + * Tests for {@link SharedHttpSessionConfigurer}. + * @author Rossen Stoyanchev + */ +public class SharedHttpSessionTests { + + + @Test + public void httpSession() throws Exception { + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController()) + .apply(sharedHttpSession()) + .build(); + + String url = "/session"; + + MvcResult result = mockMvc.perform(get(url)).andExpect(status().isOk()).andReturn(); + HttpSession session = result.getRequest().getSession(false); + assertNotNull(session); + assertEquals(1, session.getAttribute("counter")); + + result = mockMvc.perform(get(url)).andExpect(status().isOk()).andReturn(); + session = result.getRequest().getSession(false); + assertNotNull(session); + assertEquals(2, session.getAttribute("counter")); + + result = mockMvc.perform(get(url)).andExpect(status().isOk()).andReturn(); + session = result.getRequest().getSession(false); + assertNotNull(session); + assertEquals(3, session.getAttribute("counter")); + } + + @Test + public void noHttpSession() throws Exception { + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController()) + .apply(sharedHttpSession()) + .build(); + + String url = "/no-session"; + + MvcResult result = mockMvc.perform(get(url)).andExpect(status().isOk()).andReturn(); + HttpSession session = result.getRequest().getSession(false); + assertNull(session); + + result = mockMvc.perform(get(url)).andExpect(status().isOk()).andReturn(); + session = result.getRequest().getSession(false); + assertNull(session); + + url = "/session"; + + result = mockMvc.perform(get(url)).andExpect(status().isOk()).andReturn(); + session = result.getRequest().getSession(false); + assertNotNull(session); + assertEquals(1, session.getAttribute("counter")); + } + + + @Controller + private static class TestController { + + @GetMapping("/session") + public String handle(HttpSession session) { + Integer counter = (Integer) session.getAttribute("counter"); + session.setAttribute("counter", (counter != null ? counter + 1 : 1)); + return "view"; + } + + @GetMapping("/no-session") + public String handle() { + return "view"; + } + } + +} diff --git a/src/asciidoc/testing.adoc b/src/asciidoc/testing.adoc index b484b33638..ee444cd359 100644 --- a/src/asciidoc/testing.adoc +++ b/src/asciidoc/testing.adoc @@ -3822,7 +3822,7 @@ IntelliJ) may not require any additional configuration. Just check the support f completion on static members. [[spring-mvc-test-server-setup-options]] -===== Setup Options +===== Setup Choices There are two main options for creating an instance of `MockMvc`. The first is to load Spring MVC configuration through the __TestContext framework__, which loads the Spring configuration and injects a `WebApplicationContext` @@ -3927,6 +3927,48 @@ answer. However, using the "standaloneSetup" does imply the need for additional Alternatively, you may choose to write all tests with "webAppContextSetup" in order to always test against your actual Spring MVC configuration. +[[spring-mvc-test-server-setup-steps]] +===== Setup Features + +No matter which MockMvc builder you use all `MockMvcBuilder` implementations provide +some common and very useful features. For example you can declare an `Accept` header +for all requests and expect a status of 200 as well as a `Content-Type` header +in all responses as follows: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- +// static import of MockMvcBuilders.standaloneSetup + +MockMVc mockMvc = standaloneSetup(new MusicController()) + .defaultRequest(get("/").accept(MediaType.APPLICATION_JSON)) + .alwaysExpect(status().isOk()) + .alwaysExpect(content().contentType("application/json;charset=UTF-8")) + .build(); +---- + +In addition 3rd party frameworks (and applications) may pre-package setup +instructions like the ones through a `MockMvcConfigurer`. The Spring Framework +has one such built-in implementation that helps to save and re-use the HTTP +session across requests. It can be used as follows: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- +// static import of SharedHttpSessionConfigurer.sharedHttpSession + +MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController()) + .apply(sharedHttpSession()) + .build(); + +// Use mockMvc to perform requests... +---- + +See `ConfigurableMockMvcBuilder` for a list of all MockMvc builder features +or use the IDE to explore the available options. + + + [[spring-mvc-test-server-performing-requests]] ===== Performing Requests It's easy to perform requests using any HTTP method: