Skip to content

Commit f5b6514

Browse files
committed
Allow additional context interfaces to be defined for testing
Update `AssertableApplicationContext` and `ApplicationContextRunner` implementations to support additional `ApplicationContext` interfaces. Closes gh-42369
1 parent 8ac3528 commit f5b6514

18 files changed

+310
-29
lines changed

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/ApplicationContextAssertProvider.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-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.
@@ -18,12 +18,14 @@
1818

1919
import java.io.Closeable;
2020
import java.lang.reflect.Proxy;
21+
import java.util.Arrays;
2122
import java.util.function.Supplier;
2223

2324
import org.assertj.core.api.AssertProvider;
2425

2526
import org.springframework.context.ApplicationContext;
2627
import org.springframework.util.Assert;
28+
import org.springframework.util.ObjectUtils;
2729

2830
/**
2931
* An {@link ApplicationContext} that additionally supports AssertJ style assertions. Can
@@ -101,16 +103,46 @@ public interface ApplicationContextAssertProvider<C extends ApplicationContext>
101103
* {@link ApplicationContext} or throw an exception if the context fails to start.
102104
* @return a {@link ApplicationContextAssertProvider} instance
103105
*/
104-
@SuppressWarnings("unchecked")
105106
static <T extends ApplicationContextAssertProvider<C>, C extends ApplicationContext> T get(Class<T> type,
106107
Class<? extends C> contextType, Supplier<? extends C> contextSupplier) {
108+
return get(type, contextType, contextSupplier, new Class<?>[0]);
109+
}
110+
111+
/**
112+
* Factory method to create a new {@link ApplicationContextAssertProvider} instance.
113+
* @param <T> the assert provider type
114+
* @param <C> the context type
115+
* @param type the type of {@link ApplicationContextAssertProvider} required (must be
116+
* an interface)
117+
* @param contextType the type of {@link ApplicationContext} being managed (must be an
118+
* interface)
119+
* @param contextSupplier a supplier that will either return a fully configured
120+
* {@link ApplicationContext} or throw an exception if the context fails to start.
121+
* @param additionalContextInterfaces and additional context interfaces to add to the
122+
* proxy
123+
* @return a {@link ApplicationContextAssertProvider} instance
124+
* @since 3.4.0
125+
*/
126+
@SuppressWarnings("unchecked")
127+
static <T extends ApplicationContextAssertProvider<C>, C extends ApplicationContext> T get(Class<T> type,
128+
Class<? extends C> contextType, Supplier<? extends C> contextSupplier,
129+
Class<?>... additionalContextInterfaces) {
107130
Assert.notNull(type, "Type must not be null");
108131
Assert.isTrue(type.isInterface(), "Type must be an interface");
109132
Assert.notNull(contextType, "ContextType must not be null");
110133
Assert.isTrue(contextType.isInterface(), "ContextType must be an interface");
111-
Class<?>[] interfaces = { type, contextType };
134+
Class<?>[] interfaces = merge(new Class<?>[] { type, contextType }, additionalContextInterfaces);
112135
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces,
113136
new AssertProviderApplicationContextInvocationHandler(contextType, contextSupplier));
114137
}
115138

139+
private static Class<?>[] merge(Class<?>[] classes, Class<?>[] additional) {
140+
if (ObjectUtils.isEmpty(additional)) {
141+
return classes;
142+
}
143+
Class<?>[] result = Arrays.copyOf(classes, classes.length + additional.length);
144+
System.arraycopy(additional, 0, result, classes.length, additional.length);
145+
return result;
146+
}
147+
116148
}

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableApplicationContext.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-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.
@@ -49,4 +49,20 @@ static AssertableApplicationContext get(Supplier<? extends ConfigurableApplicati
4949
ConfigurableApplicationContext.class, contextSupplier);
5050
}
5151

52+
/**
53+
* Factory method to create a new {@link AssertableApplicationContext} instance.
54+
* @param contextSupplier a supplier that will either return a fully configured
55+
* {@link ConfigurableApplicationContext} or throw an exception if the context fails
56+
* to start.
57+
* @param additionalContextInterfaces and additional context interfaces to add to the
58+
* proxy
59+
* @return an {@link AssertableApplicationContext} instance
60+
* @since 3.4.0
61+
*/
62+
static AssertableApplicationContext get(Supplier<? extends ConfigurableApplicationContext> contextSupplier,
63+
Class<?>... additionalContextInterfaces) {
64+
return ApplicationContextAssertProvider.get(AssertableApplicationContext.class,
65+
ConfigurableApplicationContext.class, contextSupplier, additionalContextInterfaces);
66+
}
67+
5268
}

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableReactiveWebApplicationContext.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-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.
@@ -51,4 +51,22 @@ static AssertableReactiveWebApplicationContext get(
5151
ConfigurableReactiveWebApplicationContext.class, contextSupplier);
5252
}
5353

54+
/**
55+
* Factory method to create a new {@link AssertableReactiveWebApplicationContext}
56+
* instance.
57+
* @param contextSupplier a supplier that will either return a fully configured
58+
* {@link ConfigurableReactiveWebApplicationContext} or throw an exception if the
59+
* context fails to start.
60+
* @param additionalContextInterfaces and additional context interfaces to add to the
61+
* proxy
62+
* @return a {@link AssertableReactiveWebApplicationContext} instance
63+
* @since 3.4.0
64+
*/
65+
static AssertableReactiveWebApplicationContext get(
66+
Supplier<? extends ConfigurableReactiveWebApplicationContext> contextSupplier,
67+
Class<?>... additionalContextInterfaces) {
68+
return ApplicationContextAssertProvider.get(AssertableReactiveWebApplicationContext.class,
69+
ConfigurableReactiveWebApplicationContext.class, contextSupplier, additionalContextInterfaces);
70+
}
71+
5472
}

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/assertj/AssertableWebApplicationContext.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-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.
@@ -49,4 +49,20 @@ static AssertableWebApplicationContext get(Supplier<? extends ConfigurableWebApp
4949
ConfigurableWebApplicationContext.class, contextSupplier);
5050
}
5151

52+
/**
53+
* Factory method to create a new {@link AssertableWebApplicationContext} instance.
54+
* @param contextSupplier a supplier that will either return a fully configured
55+
* {@link ConfigurableWebApplicationContext} or throw an exception if the context
56+
* fails to start.
57+
* @param additionalContextInterfaces and additional context interfaces to add to the
58+
* proxy
59+
* @return a {@link AssertableWebApplicationContext} instance
60+
* @since 3.4.0
61+
*/
62+
static AssertableWebApplicationContext get(Supplier<? extends ConfigurableWebApplicationContext> contextSupplier,
63+
Class<?>... additionalContextInterfaces) {
64+
return ApplicationContextAssertProvider.get(AssertableWebApplicationContext.class,
65+
ConfigurableWebApplicationContext.class, contextSupplier, additionalContextInterfaces);
66+
}
67+
5268
}

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/AbstractApplicationContextRunner.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-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.
@@ -106,6 +106,8 @@
106106
*/
107107
public abstract class AbstractApplicationContextRunner<SELF extends AbstractApplicationContextRunner<SELF, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
108108

109+
private static final Class<?>[] NO_ADDITIONAL_CONTEXT_INTERFACES = {};
110+
109111
private final RunnerConfiguration<C> runnerConfiguration;
110112

111113
private final Function<RunnerConfiguration<C>, SELF> instanceFactory;
@@ -115,13 +117,29 @@ public abstract class AbstractApplicationContextRunner<SELF extends AbstractAppl
115117
* @param contextFactory the factory used to create the actual context
116118
* @param instanceFactory the factory used to create new instance of the runner
117119
* @since 2.6.0
120+
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of
121+
* {@link #AbstractApplicationContextRunner(Function, Supplier, Class...)}
118122
*/
123+
@Deprecated(since = "3.4.0", forRemoval = true)
119124
protected AbstractApplicationContextRunner(Supplier<C> contextFactory,
120125
Function<RunnerConfiguration<C>, SELF> instanceFactory) {
121-
Assert.notNull(contextFactory, "ContextFactory must not be null");
122-
Assert.notNull(contextFactory, "RunnerConfiguration must not be null");
123-
this.runnerConfiguration = new RunnerConfiguration<>(contextFactory);
126+
this(instanceFactory, contextFactory, NO_ADDITIONAL_CONTEXT_INTERFACES);
127+
}
128+
129+
/**
130+
* Create a new {@link AbstractApplicationContextRunner} instance.
131+
* @param instanceFactory the factory used to create new instance of the runner
132+
* @param contextFactory the factory used to create the actual context
133+
* @param additionalContextInterfaces any additional application context interfaces to
134+
* be added to the application context proxy
135+
* @since 3.4.0
136+
*/
137+
protected AbstractApplicationContextRunner(Function<RunnerConfiguration<C>, SELF> instanceFactory,
138+
Supplier<C> contextFactory, Class<?>... additionalContextInterfaces) {
139+
Assert.notNull(instanceFactory, "'instanceFactory' must not be null");
140+
Assert.notNull(contextFactory, "'contextFactory' must not be null");
124141
this.instanceFactory = instanceFactory;
142+
this.runnerConfiguration = new RunnerConfiguration<>(contextFactory, additionalContextInterfaces);
125143
}
126144

127145
/**
@@ -386,7 +404,8 @@ private A createAssertableContext(boolean refresh) {
386404
ResolvableType resolvableType = ResolvableType.forClass(AbstractApplicationContextRunner.class, getClass());
387405
Class<A> assertType = (Class<A>) resolvableType.resolveGeneric(1);
388406
Class<C> contextType = (Class<C>) resolvableType.resolveGeneric(2);
389-
return ApplicationContextAssertProvider.get(assertType, contextType, () -> createAndLoadContext(refresh));
407+
return ApplicationContextAssertProvider.get(assertType, contextType, () -> createAndLoadContext(refresh),
408+
this.runnerConfiguration.additionalContextInterfaces);
390409
}
391410

392411
private C createAndLoadContext(boolean refresh) {
@@ -472,6 +491,8 @@ protected static final class RunnerConfiguration<C extends ConfigurableApplicati
472491

473492
private final Supplier<C> contextFactory;
474493

494+
private final Class<?>[] additionalContextInterfaces;
495+
475496
private boolean allowBeanDefinitionOverriding = false;
476497

477498
private boolean allowCircularReferences = false;
@@ -490,12 +511,14 @@ protected static final class RunnerConfiguration<C extends ConfigurableApplicati
490511

491512
private List<Configurations> configurations = Collections.emptyList();
492513

493-
private RunnerConfiguration(Supplier<C> contextFactory) {
514+
private RunnerConfiguration(Supplier<C> contextFactory, Class<?>[] additionalContextInterfaces) {
494515
this.contextFactory = contextFactory;
516+
this.additionalContextInterfaces = additionalContextInterfaces;
495517
}
496518

497519
private RunnerConfiguration(RunnerConfiguration<C> source) {
498520
this.contextFactory = source.contextFactory;
521+
this.additionalContextInterfaces = source.additionalContextInterfaces;
499522
this.allowBeanDefinitionOverriding = source.allowBeanDefinitionOverriding;
500523
this.allowCircularReferences = source.allowCircularReferences;
501524
this.initializers = source.initializers;

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ApplicationContextRunner.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-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.
@@ -47,10 +47,24 @@ public ApplicationContextRunner() {
4747
/**
4848
* Create a new {@link ApplicationContextRunner} instance using the specified
4949
* {@code contextFactory} as the underlying source.
50-
* @param contextFactory a supplier that returns a new instance on each call
50+
* @param contextFactory a supplier that returns a new instance on each call be added
51+
* to the application context proxy
5152
*/
5253
public ApplicationContextRunner(Supplier<ConfigurableApplicationContext> contextFactory) {
53-
super(contextFactory, ApplicationContextRunner::new);
54+
super(ApplicationContextRunner::new, contextFactory);
55+
}
56+
57+
/**
58+
* Create a new {@link ApplicationContextRunner} instance using the specified
59+
* {@code contextFactory} as the underlying source.
60+
* @param contextFactory a supplier that returns a new instance on each call
61+
* @param additionalContextInterfaces any additional application context interfaces to
62+
* be added to the application context proxy
63+
* @since 3.4.0
64+
*/
65+
public ApplicationContextRunner(Supplier<ConfigurableApplicationContext> contextFactory,
66+
Class<?>... additionalContextInterfaces) {
67+
super(ApplicationContextRunner::new, contextFactory, additionalContextInterfaces);
5468
}
5569

5670
private ApplicationContextRunner(RunnerConfiguration<ConfigurableApplicationContext> runnerConfiguration) {

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/ReactiveWebApplicationContextRunner.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-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.
@@ -47,10 +47,25 @@ public ReactiveWebApplicationContextRunner() {
4747
/**
4848
* Create a new {@link ApplicationContextRunner} instance using the specified
4949
* {@code contextFactory} as the underlying source.
50-
* @param contextFactory a supplier that returns a new instance on each call
50+
* @param contextFactory a supplier that returns a new instance on each call be added
51+
* to the application context proxy
52+
* @since 3.4.0
5153
*/
5254
public ReactiveWebApplicationContextRunner(Supplier<ConfigurableReactiveWebApplicationContext> contextFactory) {
53-
super(contextFactory, ReactiveWebApplicationContextRunner::new);
55+
super(ReactiveWebApplicationContextRunner::new, contextFactory);
56+
}
57+
58+
/**
59+
* Create a new {@link ApplicationContextRunner} instance using the specified
60+
* {@code contextFactory} as the underlying source.
61+
* @param contextFactory a supplier that returns a new instance on each call
62+
* @param additionalContextInterfaces any additional application context interfaces to
63+
* be added to the application context proxy
64+
* @since 3.4.0
65+
*/
66+
public ReactiveWebApplicationContextRunner(Supplier<ConfigurableReactiveWebApplicationContext> contextFactory,
67+
Class<?>... additionalContextInterfaces) {
68+
super(ReactiveWebApplicationContextRunner::new, contextFactory, additionalContextInterfaces);
5469
}
5570

5671
private ReactiveWebApplicationContextRunner(

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/runner/WebApplicationContextRunner.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-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.
@@ -51,10 +51,24 @@ public WebApplicationContextRunner() {
5151
/**
5252
* Create a new {@link WebApplicationContextRunner} instance using the specified
5353
* {@code contextFactory} as the underlying source.
54-
* @param contextFactory a supplier that returns a new instance on each call
54+
* @param contextFactory a supplier that returns a new instance on each call be added
55+
* to the application context proxy
5556
*/
5657
public WebApplicationContextRunner(Supplier<ConfigurableWebApplicationContext> contextFactory) {
57-
super(contextFactory, WebApplicationContextRunner::new);
58+
super(WebApplicationContextRunner::new, contextFactory);
59+
}
60+
61+
/**
62+
* Create a new {@link WebApplicationContextRunner} instance using the specified
63+
* {@code contextFactory} as the underlying source.
64+
* @param contextFactory a supplier that returns a new instance on each call
65+
* @param additionalContextInterfaces any additional application context interfaces to
66+
* be added to the application context proxy
67+
* @since 3.4.0
68+
*/
69+
public WebApplicationContextRunner(Supplier<ConfigurableWebApplicationContext> contextFactory,
70+
Class<?>... additionalContextInterfaces) {
71+
super(WebApplicationContextRunner::new, contextFactory, additionalContextInterfaces);
5872
}
5973

6074
private WebApplicationContextRunner(RunnerConfiguration<ConfigurableWebApplicationContext> configuration) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2012-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.boot.test.context.assertj;
18+
19+
import org.springframework.context.ApplicationContext;
20+
21+
/**
22+
* Tests extra interface that can be applied to an {@link ApplicationContext}
23+
*
24+
* @author Phillip Webb
25+
*/
26+
interface AdditionalContextInterface {
27+
28+
}

0 commit comments

Comments
 (0)