001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.test.web.servlet; 018 019import java.util.ArrayList; 020import java.util.List; 021import javax.servlet.DispatcherType; 022import javax.servlet.Filter; 023import javax.servlet.ServletContext; 024 025import org.springframework.beans.Mergeable; 026import org.springframework.mock.web.MockFilterChain; 027import org.springframework.mock.web.MockHttpServletRequest; 028import org.springframework.mock.web.MockHttpServletResponse; 029import org.springframework.util.Assert; 030import org.springframework.web.context.request.RequestAttributes; 031import org.springframework.web.context.request.RequestContextHolder; 032import org.springframework.web.context.request.ServletRequestAttributes; 033 034/** 035 * <strong>Main entry point for server-side Spring MVC test support.</strong> 036 * 037 * <h3>Example</h3> 038 * 039 * <pre class="code"> 040 * import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; 041 * import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 042 * import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; 043 * 044 * // ... 045 * 046 * WebApplicationContext wac = ...; 047 * 048 * MockMvc mockMvc = webAppContextSetup(wac).build(); 049 * 050 * mockMvc.perform(get("/form")) 051 * .andExpect(status().isOk()) 052 * .andExpect(content().mimeType("text/html")) 053 * .andExpect(forwardedUrl("/WEB-INF/layouts/main.jsp")); 054 * </pre> 055 * 056 * @author Rossen Stoyanchev 057 * @author Rob Winch 058 * @author Sam Brannen 059 * @since 3.2 060 */ 061public final class MockMvc { 062 063 static final String MVC_RESULT_ATTRIBUTE = MockMvc.class.getName().concat(".MVC_RESULT_ATTRIBUTE"); 064 065 private final TestDispatcherServlet servlet; 066 067 private final Filter[] filters; 068 069 private final ServletContext servletContext; 070 071 private RequestBuilder defaultRequestBuilder; 072 073 private List<ResultMatcher> defaultResultMatchers = new ArrayList<ResultMatcher>(); 074 075 private List<ResultHandler> defaultResultHandlers = new ArrayList<ResultHandler>(); 076 077 078 /** 079 * Private constructor, not for direct instantiation. 080 * @see org.springframework.test.web.servlet.setup.MockMvcBuilders 081 */ 082 MockMvc(TestDispatcherServlet servlet, Filter[] filters, ServletContext servletContext) { 083 Assert.notNull(servlet, "DispatcherServlet is required"); 084 Assert.notNull(filters, "Filters cannot be null"); 085 Assert.noNullElements(filters, "Filters cannot contain null values"); 086 Assert.notNull(servletContext, "ServletContext is required"); 087 088 this.servlet = servlet; 089 this.filters = filters; 090 this.servletContext = servletContext; 091 } 092 093 094 /** 095 * A default request builder merged into every performed request. 096 * @see org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder#defaultRequest(RequestBuilder) 097 */ 098 void setDefaultRequest(RequestBuilder requestBuilder) { 099 this.defaultRequestBuilder = requestBuilder; 100 } 101 102 /** 103 * Expectations to assert after every performed request. 104 * @see org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder#alwaysExpect(ResultMatcher) 105 */ 106 void setGlobalResultMatchers(List<ResultMatcher> resultMatchers) { 107 Assert.notNull(resultMatchers, "ResultMatcher List is required"); 108 this.defaultResultMatchers = resultMatchers; 109 } 110 111 /** 112 * General actions to apply after every performed request. 113 * @see org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder#alwaysDo(ResultHandler) 114 */ 115 void setGlobalResultHandlers(List<ResultHandler> resultHandlers) { 116 Assert.notNull(resultHandlers, "ResultHandler List is required"); 117 this.defaultResultHandlers = resultHandlers; 118 } 119 120 /** 121 * Perform a request and return a type that allows chaining further 122 * actions, such as asserting expectations, on the result. 123 * @param requestBuilder used to prepare the request to execute; 124 * see static factory methods in 125 * {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders} 126 * @return an instance of {@link ResultActions} (never {@code null}) 127 * @see org.springframework.test.web.servlet.request.MockMvcRequestBuilders 128 * @see org.springframework.test.web.servlet.result.MockMvcResultMatchers 129 */ 130 public ResultActions perform(RequestBuilder requestBuilder) throws Exception { 131 if (this.defaultRequestBuilder != null) { 132 if (requestBuilder instanceof Mergeable) { 133 requestBuilder = (RequestBuilder) ((Mergeable) requestBuilder).merge(this.defaultRequestBuilder); 134 } 135 } 136 137 MockHttpServletRequest request = requestBuilder.buildRequest(this.servletContext); 138 MockHttpServletResponse response = new MockHttpServletResponse(); 139 140 if (requestBuilder instanceof SmartRequestBuilder) { 141 request = ((SmartRequestBuilder) requestBuilder).postProcessRequest(request); 142 } 143 144 final MvcResult mvcResult = new DefaultMvcResult(request, response); 145 request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult); 146 147 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 148 RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response)); 149 150 MockFilterChain filterChain = new MockFilterChain(this.servlet, this.filters); 151 filterChain.doFilter(request, response); 152 153 if (DispatcherType.ASYNC.equals(request.getDispatcherType()) && 154 request.getAsyncContext() != null && !request.isAsyncStarted()) { 155 request.getAsyncContext().complete(); 156 } 157 158 applyDefaultResultActions(mvcResult); 159 RequestContextHolder.setRequestAttributes(previousAttributes); 160 161 return new ResultActions() { 162 @Override 163 public ResultActions andExpect(ResultMatcher matcher) throws Exception { 164 matcher.match(mvcResult); 165 return this; 166 } 167 @Override 168 public ResultActions andDo(ResultHandler handler) throws Exception { 169 handler.handle(mvcResult); 170 return this; 171 } 172 @Override 173 public MvcResult andReturn() { 174 return mvcResult; 175 } 176 }; 177 } 178 179 private void applyDefaultResultActions(MvcResult mvcResult) throws Exception { 180 for (ResultMatcher matcher : this.defaultResultMatchers) { 181 matcher.match(mvcResult); 182 } 183 for (ResultHandler handler : this.defaultResultHandlers) { 184 handler.handle(mvcResult); 185 } 186 } 187 188}