Skip to content

Commit 8f3b748

Browse files
committed
Add testing support for WebMvc.fn
This commit introduces testing support for WebMvc.fn in the form of a RouterFunctionMockMvcBuilder and RouterFunctionMockMvcSpec. Closes gh-30477
1 parent d708343 commit 8f3b748

File tree

9 files changed

+875
-24
lines changed

9 files changed

+875
-24
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/client/MockMvcWebTestClient.java

+84-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
import org.springframework.test.web.servlet.ResultMatcher;
3636
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
3737
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
38+
import org.springframework.test.web.servlet.setup.RouterFunctionMockMvcBuilder;
3839
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;
3940
import org.springframework.validation.Validator;
4041
import org.springframework.web.accept.ContentNegotiationManager;
@@ -47,6 +48,7 @@
4748
import org.springframework.web.servlet.LocaleResolver;
4849
import org.springframework.web.servlet.View;
4950
import org.springframework.web.servlet.ViewResolver;
51+
import org.springframework.web.servlet.function.RouterFunction;
5052
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
5153
import org.springframework.web.util.pattern.PathPatternParser;
5254

@@ -81,13 +83,25 @@ public interface MockMvcWebTestClient {
8183
* Begin creating a {@link WebTestClient} by providing the {@code @Controller}
8284
* instance(s) to handle requests with.
8385
* <p>Internally this is delegated to and equivalent to using
84-
* {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#standaloneSetup(Object...)}.
86+
* {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#standaloneSetup(Object...)}
8587
* to initialize {@link MockMvc}.
8688
*/
8789
static ControllerSpec bindToController(Object... controllers) {
8890
return new StandaloneMockMvcSpec(controllers);
8991
}
9092

93+
/**
94+
* Begin creating a {@link WebTestClient} by providing the {@link RouterFunction}
95+
* instance(s) to handle requests with.
96+
* <p>Internally this is delegated to and equivalent to using
97+
* {@link org.springframework.test.web.servlet.setup.MockMvcBuilders#routerFunctions(RouterFunction[])}
98+
* to initialize {@link MockMvc}.
99+
* @since 6.2
100+
*/
101+
static RouterFunctionSpec bindToRouterFunction(RouterFunction<?>... routerFunctions) {
102+
return new RouterFunctionMockMvcSpec(routerFunctions);
103+
}
104+
91105
/**
92106
* Begin creating a {@link WebTestClient} by providing a
93107
* {@link WebApplicationContext} with Spring MVC infrastructure and
@@ -381,4 +395,72 @@ ControllerSpec mappedInterceptors(
381395
ControllerSpec customHandlerMapping(Supplier<RequestMappingHandlerMapping> factory);
382396
}
383397

398+
399+
/**
400+
* Specification for configuring {@link MockMvc} to test one or more
401+
* {@linkplain RouterFunction router functions}
402+
* directly, and a simple facade around {@link RouterFunctionMockMvcBuilder}.
403+
* @since 6.2
404+
*/
405+
interface RouterFunctionSpec extends MockMvcServerSpec<RouterFunctionSpec> {
406+
407+
/**
408+
* Set the message converters to use.
409+
* <p>This is delegated to
410+
* {@link RouterFunctionMockMvcBuilder#setMessageConverters(HttpMessageConverter[])}.
411+
*/
412+
RouterFunctionSpec messageConverters(HttpMessageConverter<?>... messageConverters);
413+
414+
/**
415+
* Add global interceptors.
416+
* <p>This is delegated to
417+
* {@link RouterFunctionMockMvcBuilder#addInterceptors(HandlerInterceptor...)}.
418+
*/
419+
RouterFunctionSpec interceptors(HandlerInterceptor... interceptors);
420+
421+
/**
422+
* Add interceptors for specific patterns.
423+
* <p>This is delegated to
424+
* {@link RouterFunctionMockMvcBuilder#addMappedInterceptors(String[], HandlerInterceptor...)}.
425+
*/
426+
RouterFunctionSpec mappedInterceptors(
427+
@Nullable String[] pathPatterns, HandlerInterceptor... interceptors);
428+
429+
/**
430+
* Specify the timeout value for async execution.
431+
* <p>This is delegated to
432+
* {@link RouterFunctionMockMvcBuilder#setAsyncRequestTimeout(long)}.
433+
*/
434+
RouterFunctionSpec asyncRequestTimeout(long timeout);
435+
436+
/**
437+
* Set the HandlerExceptionResolver types to use.
438+
* <p>This is delegated to
439+
* {@link RouterFunctionMockMvcBuilder#setHandlerExceptionResolvers(HandlerExceptionResolver...)}.
440+
*/
441+
RouterFunctionSpec handlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers);
442+
443+
/**
444+
* Set up view resolution.
445+
* <p>This is delegated to
446+
* {@link RouterFunctionMockMvcBuilder#setViewResolvers(ViewResolver...)}.
447+
*/
448+
RouterFunctionSpec viewResolvers(ViewResolver... resolvers);
449+
450+
/**
451+
* Set up a single {@link ViewResolver} with a fixed view.
452+
* <p>This is delegated to
453+
* {@link RouterFunctionMockMvcBuilder#setSingleView(View)}.
454+
*/
455+
RouterFunctionSpec singleView(View view);
456+
457+
/**
458+
* Enable URL path matching with parsed
459+
* {@link org.springframework.web.util.pattern.PathPattern PathPatterns}.
460+
* <p>This is delegated to
461+
* {@link RouterFunctionMockMvcBuilder#setPatternParser(PathPatternParser)}.
462+
*/
463+
RouterFunctionSpec patternParser(PathPatternParser parser);
464+
}
465+
384466
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.web.servlet.client;
18+
19+
import org.springframework.http.converter.HttpMessageConverter;
20+
import org.springframework.lang.Nullable;
21+
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
22+
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23+
import org.springframework.test.web.servlet.setup.RouterFunctionMockMvcBuilder;
24+
import org.springframework.web.servlet.HandlerExceptionResolver;
25+
import org.springframework.web.servlet.HandlerInterceptor;
26+
import org.springframework.web.servlet.View;
27+
import org.springframework.web.servlet.ViewResolver;
28+
import org.springframework.web.servlet.function.RouterFunction;
29+
import org.springframework.web.util.pattern.PathPatternParser;
30+
31+
/**
32+
* Simple wrapper around a {@link RouterFunctionMockMvcBuilder} that implements
33+
* {@link MockMvcWebTestClient.RouterFunctionSpec}.
34+
*
35+
* @author Arjen Poutsma
36+
* @since 6.2
37+
*/
38+
class RouterFunctionMockMvcSpec extends AbstractMockMvcServerSpec<MockMvcWebTestClient.RouterFunctionSpec>
39+
implements MockMvcWebTestClient.RouterFunctionSpec {
40+
41+
private final RouterFunctionMockMvcBuilder mockMvcBuilder;
42+
43+
44+
RouterFunctionMockMvcSpec(RouterFunction<?>... routerFunctions) {
45+
this.mockMvcBuilder = MockMvcBuilders.routerFunctions(routerFunctions);
46+
}
47+
48+
@Override
49+
public MockMvcWebTestClient.RouterFunctionSpec messageConverters(HttpMessageConverter<?>... messageConverters) {
50+
this.mockMvcBuilder.setMessageConverters(messageConverters);
51+
return this;
52+
}
53+
54+
@Override
55+
public MockMvcWebTestClient.RouterFunctionSpec interceptors(HandlerInterceptor... interceptors) {
56+
mappedInterceptors(null, interceptors);
57+
return this;
58+
}
59+
60+
@Override
61+
public MockMvcWebTestClient.RouterFunctionSpec mappedInterceptors(@Nullable String[] pathPatterns, HandlerInterceptor... interceptors) {
62+
this.mockMvcBuilder.addMappedInterceptors(pathPatterns, interceptors);
63+
return this;
64+
}
65+
66+
@Override
67+
public MockMvcWebTestClient.RouterFunctionSpec asyncRequestTimeout(long timeout) {
68+
this.mockMvcBuilder.setAsyncRequestTimeout(timeout);
69+
return this;
70+
}
71+
72+
@Override
73+
public MockMvcWebTestClient.RouterFunctionSpec handlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers) {
74+
this.mockMvcBuilder.setHandlerExceptionResolvers(exceptionResolvers);
75+
return this;
76+
}
77+
78+
@Override
79+
public MockMvcWebTestClient.RouterFunctionSpec viewResolvers(ViewResolver... resolvers) {
80+
this.mockMvcBuilder.setViewResolvers(resolvers);
81+
return this;
82+
}
83+
84+
@Override
85+
public MockMvcWebTestClient.RouterFunctionSpec singleView(View view) {
86+
this.mockMvcBuilder.setSingleView(view);
87+
return this;
88+
}
89+
90+
@Override
91+
public MockMvcWebTestClient.RouterFunctionSpec patternParser(PathPatternParser parser) {
92+
this.mockMvcBuilder.setPatternParser(parser);
93+
return this;
94+
}
95+
96+
@Override
97+
protected ConfigurableMockMvcBuilder<?> getMockMvcBuilder() {
98+
return this.mockMvcBuilder;
99+
}
100+
}

spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcBuilders.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import org.springframework.test.web.servlet.MockMvc;
2020
import org.springframework.test.web.servlet.MockMvcBuilder;
2121
import org.springframework.web.context.WebApplicationContext;
22+
import org.springframework.web.servlet.function.RouterFunction;
2223

2324
/**
2425
* The main class to import in order to access all available {@link MockMvcBuilder MockMvcBuilders}.
@@ -76,4 +77,23 @@ public static StandaloneMockMvcBuilder standaloneSetup(Object... controllers) {
7677
return new StandaloneMockMvcBuilder(controllers);
7778
}
7879

80+
/**
81+
* Build a {@link MockMvc} instance by registering one or more
82+
* {@link RouterFunction RouterFunction} instances and configuring Spring
83+
* MVC infrastructure programmatically.
84+
* <p>This allows full control over the instantiation and initialization of
85+
* router functions and their dependencies, similar to plain unit tests while
86+
* also making it possible to test one router function at a time.
87+
* <p>When this builder is used, the minimum infrastructure required by the
88+
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}
89+
* to serve requests with router functions is created automatically
90+
* and can be customized, resulting in configuration that is equivalent to
91+
* what MVC Java configuration provides except using builder-style methods.
92+
* @param routerFunctions one or more {@code RouterFunction} instances to test
93+
* @since 6.2
94+
*/
95+
public static RouterFunctionMockMvcBuilder routerFunctions(RouterFunction<?>... routerFunctions) {
96+
return new RouterFunctionMockMvcBuilder(routerFunctions);
97+
}
98+
7999
}

0 commit comments

Comments
 (0)