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.setup; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import javax.servlet.Filter; 023import javax.servlet.ServletContext; 024 025import org.springframework.lang.Nullable; 026import org.springframework.mock.web.MockServletConfig; 027import org.springframework.test.web.servlet.DispatcherServletCustomizer; 028import org.springframework.test.web.servlet.MockMvc; 029import org.springframework.test.web.servlet.MockMvcBuilder; 030import org.springframework.test.web.servlet.MockMvcBuilderSupport; 031import org.springframework.test.web.servlet.RequestBuilder; 032import org.springframework.test.web.servlet.ResultHandler; 033import org.springframework.test.web.servlet.ResultMatcher; 034import org.springframework.test.web.servlet.request.ConfigurableSmartRequestBuilder; 035import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 036import org.springframework.test.web.servlet.request.RequestPostProcessor; 037import org.springframework.util.Assert; 038import org.springframework.web.context.WebApplicationContext; 039 040/** 041 * Abstract implementation of {@link MockMvcBuilder} with common methods for 042 * configuring filters, default request properties, global expectations and 043 * global result actions. 044 * 045 * <p>Subclasses can use different strategies to prepare the Spring 046 * {@code WebApplicationContext} that will be passed to the 047 * {@code DispatcherServlet}. 048 * 049 * @author Rossen Stoyanchev 050 * @author Stephane Nicoll 051 * @since 4.0 052 * @param <B> a self reference to the builder type 053 */ 054public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>> 055 extends MockMvcBuilderSupport implements ConfigurableMockMvcBuilder<B> { 056 057 private List<Filter> filters = new ArrayList<>(); 058 059 @Nullable 060 private RequestBuilder defaultRequestBuilder; 061 062 private final List<ResultMatcher> globalResultMatchers = new ArrayList<>(); 063 064 private final List<ResultHandler> globalResultHandlers = new ArrayList<>(); 065 066 private final List<DispatcherServletCustomizer> dispatcherServletCustomizers = new ArrayList<>(); 067 068 private final List<MockMvcConfigurer> configurers = new ArrayList<>(4); 069 070 071 @Override 072 public final <T extends B> T addFilters(Filter... filters) { 073 Assert.notNull(filters, "filters cannot be null"); 074 for (Filter f : filters) { 075 Assert.notNull(f, "filters cannot contain null values"); 076 this.filters.add(f); 077 } 078 return self(); 079 } 080 081 @Override 082 public final <T extends B> T addFilter(Filter filter, String... urlPatterns) { 083 Assert.notNull(filter, "filter cannot be null"); 084 Assert.notNull(urlPatterns, "urlPatterns cannot be null"); 085 if (urlPatterns.length > 0) { 086 filter = new PatternMappingFilterProxy(filter, urlPatterns); 087 } 088 this.filters.add(filter); 089 return self(); 090 } 091 092 @Override 093 public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) { 094 this.defaultRequestBuilder = requestBuilder; 095 return self(); 096 } 097 098 @Override 099 public final <T extends B> T alwaysExpect(ResultMatcher resultMatcher) { 100 this.globalResultMatchers.add(resultMatcher); 101 return self(); 102 } 103 104 @Override 105 public final <T extends B> T alwaysDo(ResultHandler resultHandler) { 106 this.globalResultHandlers.add(resultHandler); 107 return self(); 108 } 109 110 public final <T extends B> T addDispatcherServletCustomizer(DispatcherServletCustomizer customizer) { 111 this.dispatcherServletCustomizers.add(customizer); 112 return self(); 113 } 114 115 @Override 116 public final <T extends B> T dispatchOptions(boolean dispatchOptions) { 117 return addDispatcherServletCustomizer( 118 dispatcherServlet -> dispatcherServlet.setDispatchOptionsRequest(dispatchOptions)); 119 } 120 121 @Override 122 public final <T extends B> T apply(MockMvcConfigurer configurer) { 123 configurer.afterConfigurerAdded(this); 124 this.configurers.add(configurer); 125 return self(); 126 } 127 128 @SuppressWarnings("unchecked") 129 protected <T extends B> T self() { 130 return (T) this; 131 } 132 133 134 /** 135 * Build a {@link org.springframework.test.web.servlet.MockMvc} instance. 136 */ 137 @Override 138 @SuppressWarnings("rawtypes") 139 public final MockMvc build() { 140 WebApplicationContext wac = initWebAppContext(); 141 ServletContext servletContext = wac.getServletContext(); 142 MockServletConfig mockServletConfig = new MockServletConfig(servletContext); 143 144 for (MockMvcConfigurer configurer : this.configurers) { 145 RequestPostProcessor processor = configurer.beforeMockMvcCreated(this, wac); 146 if (processor != null) { 147 if (this.defaultRequestBuilder == null) { 148 this.defaultRequestBuilder = MockMvcRequestBuilders.get("/"); 149 } 150 if (this.defaultRequestBuilder instanceof ConfigurableSmartRequestBuilder) { 151 ((ConfigurableSmartRequestBuilder) this.defaultRequestBuilder).with(processor); 152 } 153 } 154 } 155 156 Filter[] filterArray = this.filters.toArray(new Filter[0]); 157 158 return super.createMockMvc(filterArray, mockServletConfig, wac, this.defaultRequestBuilder, 159 this.globalResultMatchers, this.globalResultHandlers, this.dispatcherServletCustomizers); 160 } 161 162 /** 163 * A method to obtain the {@code WebApplicationContext} to be passed to the 164 * {@code DispatcherServlet}. Invoked from {@link #build()} before the 165 * {@link MockMvc} instance is created. 166 */ 167 protected abstract WebApplicationContext initWebAppContext(); 168 169}