diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java index 82ad8587097..9a206edc2f0 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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,6 +35,7 @@ import org.springframework.test.web.servlet.ResultHandler; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; import org.springframework.test.web.servlet.setup.MockMvcConfigurer; +import org.springframework.test.web.servlet.setup.RouterFunctionMockMvcBuilder; import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder; import org.springframework.validation.Validator; import org.springframework.web.accept.ContentNegotiationManager; @@ -47,6 +48,7 @@ import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.function.RouterFunction; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.util.pattern.PathPatternParser; @@ -81,13 +83,25 @@ public interface MockMvcWebTestClient { * Begin creating a {@link WebTestClient} by providing the {@code @Controller} * instance(s) to handle requests with. *
Internally this is delegated to and equivalent to using - * {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#standaloneSetup(Object...)}. + * {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#standaloneSetup(Object...)} * to initialize {@link MockMvc}. */ static ControllerSpec bindToController(Object... controllers) { return new StandaloneMockMvcSpec(controllers); } + /** + * Begin creating a {@link WebTestClient} by providing the {@link RouterFunction} + * instance(s) to handle requests with. + *
Internally this is delegated to and equivalent to using
+ * {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#routerFunctions(RouterFunction[])}
+ * to initialize {@link MockMvc}.
+ * @since 6.2
+ */
+ static RouterFunctionSpec bindToRouterFunction(RouterFunction>... routerFunctions) {
+ return new RouterFunctionMockMvcSpec(routerFunctions);
+ }
+
/**
* Begin creating a {@link WebTestClient} by providing a
* {@link WebApplicationContext} with Spring MVC infrastructure and
@@ -381,4 +395,72 @@ public interface MockMvcWebTestClient {
ControllerSpec customHandlerMapping(Supplier This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#setMessageConverters(HttpMessageConverter[])}.
+ */
+ RouterFunctionSpec messageConverters(HttpMessageConverter>... messageConverters);
+
+ /**
+ * Add global interceptors.
+ * This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#addInterceptors(HandlerInterceptor...)}.
+ */
+ RouterFunctionSpec interceptors(HandlerInterceptor... interceptors);
+
+ /**
+ * Add interceptors for specific patterns.
+ * This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#addMappedInterceptors(String[], HandlerInterceptor...)}.
+ */
+ RouterFunctionSpec mappedInterceptors(
+ @Nullable String[] pathPatterns, HandlerInterceptor... interceptors);
+
+ /**
+ * Specify the timeout value for async execution.
+ * This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#setAsyncRequestTimeout(long)}.
+ */
+ RouterFunctionSpec asyncRequestTimeout(long timeout);
+
+ /**
+ * Set the HandlerExceptionResolver types to use.
+ * This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#setHandlerExceptionResolvers(HandlerExceptionResolver...)}.
+ */
+ RouterFunctionSpec handlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers);
+
+ /**
+ * Set up view resolution.
+ * This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#setViewResolvers(ViewResolver...)}.
+ */
+ RouterFunctionSpec viewResolvers(ViewResolver... resolvers);
+
+ /**
+ * Set up a single {@link ViewResolver} with a fixed view.
+ * This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#setSingleView(View)}.
+ */
+ RouterFunctionSpec singleView(View view);
+
+ /**
+ * Enable URL path matching with parsed
+ * {@link org.springframework.web.util.pattern.PathPattern PathPatterns}.
+ * This is delegated to
+ * {@link RouterFunctionMockMvcBuilder#setPatternParser(PathPatternParser)}.
+ */
+ RouterFunctionSpec patternParser(PathPatternParser parser);
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/client/RouterFunctionMockMvcSpec.java b/spring-test/src/main/java/org/springframework/test/web/servlet/client/RouterFunctionMockMvcSpec.java
new file mode 100644
index 00000000000..5ff3e1dfc2b
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/client/RouterFunctionMockMvcSpec.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2002-2024 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
+ *
+ * https://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.client;
+
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.lang.Nullable;
+import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.test.web.servlet.setup.RouterFunctionMockMvcBuilder;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.View;
+import org.springframework.web.servlet.ViewResolver;
+import org.springframework.web.servlet.function.RouterFunction;
+import org.springframework.web.util.pattern.PathPatternParser;
+
+/**
+ * Simple wrapper around a {@link RouterFunctionMockMvcBuilder} that implements
+ * {@link MockMvcWebTestClient.RouterFunctionSpec}.
+ *
+ * @author Arjen Poutsma
+ * @since 6.2
+ */
+class RouterFunctionMockMvcSpec extends AbstractMockMvcServerSpec This allows full control over the instantiation and initialization of
+ * router functions and their dependencies, similar to plain unit tests while
+ * also making it possible to test one router function at a time.
+ * When this builder is used, the minimum infrastructure required by the
+ * {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}
+ * to serve requests with router functions is created automatically
+ * and can be customized, resulting in configuration that is equivalent to
+ * what MVC Java configuration provides except using builder-style methods.
+ * @param routerFunctions one or more {@code RouterFunction} instances to test
+ * @since 6.2
+ */
+ public static RouterFunctionMockMvcBuilder routerFunctions(RouterFunction>... routerFunctions) {
+ return new RouterFunctionMockMvcBuilder(routerFunctions);
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/RouterFunctionMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/RouterFunctionMockMvcBuilder.java
new file mode 100644
index 00000000000..6d9b507ce69
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/RouterFunctionMockMvcBuilder.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2002-2024 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
+ *
+ * https://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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+
+import jakarta.servlet.ServletContext;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.format.support.FormattingConversionService;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.lang.Nullable;
+import org.springframework.mock.web.MockServletContext;
+import org.springframework.util.Assert;
+import org.springframework.web.accept.ContentNegotiationManager;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationObjectSupport;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.View;
+import org.springframework.web.servlet.ViewResolver;
+import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+import org.springframework.web.servlet.function.RouterFunction;
+import org.springframework.web.servlet.function.support.HandlerFunctionAdapter;
+import org.springframework.web.servlet.function.support.RouterFunctionMapping;
+import org.springframework.web.servlet.handler.MappedInterceptor;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.springframework.web.servlet.resource.ResourceUrlProvider;
+import org.springframework.web.servlet.view.InternalResourceViewResolver;
+import org.springframework.web.util.pattern.PathPatternParser;
+
+/**
+ * A {@code MockMvcBuilder} that accepts {@link RouterFunction} registrations
+ * thus allowing full control over the instantiation and initialization of
+ * router functions and their dependencies similar to plain unit tests, and also
+ * making it possible to test one function at a time.
+ *
+ * This builder creates the minimum infrastructure required by the
+ * {@link DispatcherServlet} to serve requests with router functions and
+ * also provides methods for customization. The resulting configuration and
+ * customization options are equivalent to using MVC Java config except
+ * using builder style methods.
+ *
+ * To configure view resolution, either select a "fixed" view to use for every
+ * request performed (see {@link #setSingleView(View)}) or provide a list of
+ * {@code ViewResolver}s (see {@link #setViewResolvers(ViewResolver...)}).
+ *
+ * @author Arjen Poutsma
+ * @since 6.2
+ */
+public class RouterFunctionMockMvcBuilder extends AbstractMockMvcBuilder