Merge pull request #50500 from scordio

* ApplicationContextAssertProvider-generics:
  Polish "Add self-reference to ApplicationContextAssertProvider"
  Add self-reference to ApplicationContextAssertProvider

Closes gh-50500
This commit is contained in:
Stéphane Nicoll
2026-07-15 13:21:53 +02:00
11 changed files with 47 additions and 35 deletions
@@ -44,16 +44,18 @@ import org.springframework.util.ObjectUtils;
* Any {@link ApplicationContext} method called on a context that has failed to start will
* throw an {@link IllegalStateException}.
*
* @param <C> the application context type
* @param <SELF> the self class reference
* @param <C> the source application context type
* @author Phillip Webb
* @author Stefano Cordio
* @since 2.0.0
* @see AssertableApplicationContext
* @see AssertableWebApplicationContext
* @see AssertableReactiveWebApplicationContext
* @see ApplicationContextAssert
*/
public interface ApplicationContextAssertProvider<C extends ApplicationContext>
extends ApplicationContext, AssertProvider<ApplicationContextAssert<C>>, Closeable {
public interface ApplicationContextAssertProvider<SELF extends ApplicationContextAssertProvider<SELF, C>, C extends ApplicationContext>
extends ApplicationContext, AssertProvider<ApplicationContextAssert<SELF>>, Closeable {
/**
* Return an assert for AssertJ.
@@ -63,7 +65,7 @@ public interface ApplicationContextAssertProvider<C extends ApplicationContext>
*/
@Deprecated(since = "2.0.0", forRemoval = false)
@Override
ApplicationContextAssert<C> assertThat();
ApplicationContextAssert<SELF> assertThat();
/**
* Return the original source {@link ApplicationContext}.
@@ -104,7 +106,7 @@ public interface ApplicationContextAssertProvider<C extends ApplicationContext>
* {@link ApplicationContext} or throw an exception if the context fails to start.
* @return a {@link ApplicationContextAssertProvider} instance
*/
static <T extends ApplicationContextAssertProvider<C>, C extends ApplicationContext> T get(Class<T> type,
static <T extends ApplicationContextAssertProvider<? super T, C>, C extends ApplicationContext> T get(Class<T> type,
Class<? extends C> contextType, Supplier<? extends C> contextSupplier) {
return get(type, contextType, contextSupplier, new Class<?>[0]);
}
@@ -125,7 +127,7 @@ public interface ApplicationContextAssertProvider<C extends ApplicationContext>
* @since 3.4.0
*/
@SuppressWarnings("unchecked")
static <T extends ApplicationContextAssertProvider<C>, C extends ApplicationContext> T get(Class<T> type,
static <T extends ApplicationContextAssertProvider<? super T, C>, C extends ApplicationContext> T get(Class<T> type,
Class<? extends C> contextType, Supplier<? extends C> contextSupplier,
Class<?>... additionalContextInterfaces) {
Assert.notNull(type, "'type' must not be null");
@@ -35,7 +35,8 @@ import org.springframework.context.ConfigurableApplicationContext;
* @see ApplicationContext
*/
public interface AssertableApplicationContext
extends ApplicationContextAssertProvider<ConfigurableApplicationContext>, ConfigurableApplicationContext {
extends ApplicationContextAssertProvider<AssertableApplicationContext, ConfigurableApplicationContext>,
ConfigurableApplicationContext {
/**
* Factory method to create a new {@link AssertableApplicationContext} instance.
@@ -33,8 +33,8 @@ import org.springframework.boot.web.context.reactive.ReactiveWebApplicationConte
* @see ReactiveWebApplicationContext
* @see ReactiveWebApplicationContext
*/
public interface AssertableReactiveWebApplicationContext
extends ApplicationContextAssertProvider<ConfigurableReactiveWebApplicationContext>,
public interface AssertableReactiveWebApplicationContext extends
ApplicationContextAssertProvider<AssertableReactiveWebApplicationContext, ConfigurableReactiveWebApplicationContext>,
ConfigurableReactiveWebApplicationContext {
/**
@@ -35,7 +35,8 @@ import org.springframework.web.context.WebApplicationContext;
* @see WebApplicationContext
*/
public interface AssertableWebApplicationContext
extends ApplicationContextAssertProvider<ConfigurableWebApplicationContext>, ConfigurableWebApplicationContext {
extends ApplicationContextAssertProvider<AssertableWebApplicationContext, ConfigurableWebApplicationContext>,
ConfigurableWebApplicationContext {
/**
* Factory method to create a new {@link AssertableWebApplicationContext} instance.
@@ -110,7 +110,7 @@ import org.springframework.util.CollectionUtils;
* @see ReactiveWebApplicationContextRunner
* @see ApplicationContextAssert
*/
public abstract class AbstractApplicationContextRunner<SELF extends AbstractApplicationContextRunner<SELF, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
public abstract class AbstractApplicationContextRunner<SELF extends AbstractApplicationContextRunner<SELF, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<A, C>> {
private final RunnerConfiguration<C> runnerConfiguration;
@@ -100,7 +100,7 @@ class ApplicationContextAssertProviderTests {
@Test
void getWhenContextStartsShouldReturnProxyThatCallsRealMethods() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.mockContextSupplier);
assertThat((Object) context).isNotNull();
context.getBean("foo");
then(this.mockContext).should().getBean("foo");
@@ -108,7 +108,7 @@ class ApplicationContextAssertProviderTests {
@Test
void getWhenContextFailsShouldReturnProxyThatThrowsExceptions() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.startupFailureSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.startupFailureSupplier);
assertThat((Object) context).isNotNull();
assertThatIllegalStateException().isThrownBy(() -> context.getBean("foo"))
.withCause(this.startupFailure)
@@ -117,13 +117,13 @@ class ApplicationContextAssertProviderTests {
@Test
void getSourceContextWhenContextStartsShouldReturnSourceContext() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.mockContextSupplier);
assertThat(context.getSourceApplicationContext()).isSameAs(this.mockContext);
}
@Test
void getSourceContextWhenContextFailsShouldThrowException() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.startupFailureSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.startupFailureSupplier);
assertThatIllegalStateException().isThrownBy(context::getSourceApplicationContext)
.withCause(this.startupFailure)
.withMessageContaining("failed to start");
@@ -131,13 +131,13 @@ class ApplicationContextAssertProviderTests {
@Test
void getSourceContextOfTypeWhenContextStartsShouldReturnSourceContext() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.mockContextSupplier);
assertThat(context.getSourceApplicationContext(ApplicationContext.class)).isSameAs(this.mockContext);
}
@Test
void getSourceContextOfTypeWhenContextFailsToStartShouldThrowException() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.startupFailureSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.startupFailureSupplier);
assertThatIllegalStateException()
.isThrownBy(() -> context.getSourceApplicationContext(ApplicationContext.class))
.withCause(this.startupFailure)
@@ -146,59 +146,60 @@ class ApplicationContextAssertProviderTests {
@Test
void getStartupFailureWhenContextStartsShouldReturnNull() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.mockContextSupplier);
assertThat(context.getStartupFailure()).isNull();
}
@Test
void getStartupFailureWhenContextFailsToStartShouldReturnException() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.startupFailureSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.startupFailureSupplier);
assertThat(context.getStartupFailure()).isEqualTo(this.startupFailure);
}
@Test
void assertThatWhenContextStartsShouldReturnAssertions() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssert<ApplicationContext> contextAssert = assertThat(context);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssert<?> contextAssert = assertThat(context);
assertThat(contextAssert.getApplicationContext()).isSameAs(context);
assertThat(contextAssert.getStartupFailure()).isNull();
}
@Test
void assertThatWhenContextFailsShouldReturnAssertions() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.startupFailureSupplier);
ApplicationContextAssert<ApplicationContext> contextAssert = assertThat(context);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.startupFailureSupplier);
ApplicationContextAssert<?> contextAssert = assertThat(context);
assertThat(contextAssert.getApplicationContext()).isSameAs(context);
assertThat(contextAssert.getStartupFailure()).isSameAs(this.startupFailure);
}
@Test
void toStringWhenContextStartsShouldReturnSimpleString() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.mockContextSupplier);
assertThat(context.toString()).startsWith("Started application [ConfigurableApplicationContext.MockitoMock")
.endsWith("id = [null], applicationName = [null], beanDefinitionCount = 0]");
}
@Test
void toStringWhenContextFailsToStartShouldReturnSimpleString() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.startupFailureSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.startupFailureSupplier);
assertThat(context).hasToString("Unstarted application context "
+ "org.springframework.context.ApplicationContext[startupFailure=java.lang.RuntimeException]");
}
@Test
void closeShouldCloseContext() {
ApplicationContextAssertProvider<ApplicationContext> context = get(this.mockContextSupplier);
ApplicationContextAssertProvider<?, ApplicationContext> context = get(this.mockContextSupplier);
context.close();
then(this.mockContext).should().close();
}
private ApplicationContextAssertProvider<ApplicationContext> get(Supplier<ApplicationContext> contextSupplier) {
private ApplicationContextAssertProvider<?, ApplicationContext> get(Supplier<ApplicationContext> contextSupplier) {
return ApplicationContextAssertProvider.get(TestAssertProviderApplicationContext.class,
ApplicationContext.class, contextSupplier);
}
interface TestAssertProviderApplicationContext extends ApplicationContextAssertProvider<ApplicationContext> {
interface TestAssertProviderApplicationContext
extends ApplicationContextAssertProvider<TestAssertProviderApplicationContext, ApplicationContext> {
}
@@ -62,8 +62,9 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @param <A> the assertable context type
* @author Stephane Nicoll
* @author Phillip Webb
* @author Stefano Cordio
*/
abstract class AbstractApplicationContextRunnerTests<T extends AbstractApplicationContextRunner<T, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
abstract class AbstractApplicationContextRunnerTests<T extends AbstractApplicationContextRunner<T, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<A, C>> {
@Test
void runWithInitializerShouldInitialize() {
@@ -278,6 +279,12 @@ abstract class AbstractApplicationContextRunnerTests<T extends AbstractApplicati
});
}
@Test
void runShouldWorkWithSatisfiesAssertion() {
get().run((context) -> assertThat(context).satisfies((ctx) -> assertThat(ctx).hasNotFailed(),
(ctx) -> assertThat(ctx).doesNotHaveBean("foo")));
}
@Test
void changesMadeByInitializersShouldBeVisibleToRegisteredClasses() {
get().withInitializer((context) -> context.getEnvironment().setActiveProfiles("test"))
@@ -37,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
* @param <A> the assertions
* @author Madhura Bhave
*/
public abstract class AbstractHealthEndpointAdditionalPathIntegrationTests<T extends AbstractApplicationContextRunner<T, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
public abstract class AbstractHealthEndpointAdditionalPathIntegrationTests<T extends AbstractApplicationContextRunner<T, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<A, C>> {
private final T runner;
@@ -350,7 +350,7 @@ class GrpcServerAutoConfigurationTests {
"nettyGrpcServerLifecycle"));
}
private ContextConsumer<? super ApplicationContextAssertProvider<?>> assertThatServerIsConfigured(
private ContextConsumer<? super ApplicationContextAssertProvider<?, ?>> assertThatServerIsConfigured(
Class<?> expectedServerFactoryType, String expectedAddress, String expectedLifecycleBeanName) {
return (context) -> {
assertThat(context).getBean(GrpcServerFactory.class)
@@ -365,12 +365,12 @@ class GrpcServerAutoConfigurationTests {
};
}
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> R serviceBean(
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<A, C>> R serviceBean(
R contextRunner) {
return contextRunner.withBean(BindableService.class, () -> this.service);
}
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> R noOpLifecycleBeans(
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<A, C>> R noOpLifecycleBeans(
R contextRunner) {
return contextRunner.withBean("shadedNettyGrpcServerLifecycle", GrpcServerLifecycle.class, Mockito::mock)
.withBean("nettyGrpcServerLifecycle", GrpcServerLifecycle.class, Mockito::mock)
@@ -348,7 +348,7 @@ class GrpcServerHealthAutoConfigurationTests {
assertThat(context).doesNotHaveBean(GrpcServerHealthAutoConfiguration.class);
}
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> R serviceBean(
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<A, C>> R serviceBean(
R contextRunner) {
return contextRunner.withBean(BindableService.class, () -> this.service);
}
@@ -115,7 +115,7 @@ class GrpcServerOAuth2ResourceServerAutoConfigurationTests {
.run((context) -> assertThat(context).doesNotHaveBean(AuthenticationProcessInterceptor.class));
}
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> R serviceBean(
private <R extends AbstractApplicationContextRunner<R, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<A, C>> R serviceBean(
R contextRunner) {
BindableService service = mock();
ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("my-service").build();