Apply initializer automatically for context tests

Update `ServiceConnectionContextCustomizer` so that is applies the
`TestcontainersLifecycleApplicationContextInitializer` to all
application contexts.

Closes gh-35222
This commit is contained in:
Phillip Webb 2023-05-01 17:35:23 -07:00
parent 632c5d7ea5
commit 14bc354f7f
5 changed files with 36 additions and 3 deletions

View File

@ -16,6 +16,8 @@
package org.springframework.boot.testcontainers.lifecycle;
import java.util.WeakHashMap;
import org.testcontainers.lifecycle.Startable;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -32,8 +34,15 @@ import org.springframework.context.ConfigurableApplicationContext;
public class TestcontainersLifecycleApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static WeakHashMap<ConfigurableApplicationContext, Boolean> applied = new WeakHashMap<>();
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
synchronized (applied) {
if (applied.put(applicationContext, Boolean.TRUE) == Boolean.TRUE) {
return;
}
}
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
applicationContext.addBeanFactoryPostProcessor(new TestcontainersLifecycleBeanFactoryPostProcessor());
beanFactory.addBeanPostProcessor(new TestcontainersLifecycleBeanPostProcessor(beanFactory));

View File

@ -26,6 +26,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
import org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
@ -58,6 +59,7 @@ class ServiceConnectionContextCustomizer implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
new TestcontainersLifecycleApplicationContextInitializer().initialize(context);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory instanceof BeanDefinitionRegistry registry) {
new ConnectionDetailsRegistrar(beanFactory, this.connectionDetailsFactories)

View File

@ -49,7 +49,7 @@ class ServiceConnectionContextCustomizerFactory implements ContextCustomizerFact
List<ContextConfigurationAttributes> configAttributes) {
List<ContainerConnectionSource<?>> sources = new ArrayList<>();
findSources(testClass, sources);
return (sources.isEmpty()) ? null : new ServiceConnectionContextCustomizer(sources);
return new ServiceConnectionContextCustomizer(sources);
}
private void findSources(Class<?> clazz, List<ContainerConnectionSource<?>> sources) {

View File

@ -24,6 +24,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
@ -84,6 +85,16 @@ class TestcontainersLifecycleApplicationContextInitializerTests {
then(container).should(never()).close();
}
@Test
void doesNotInitializeSameContextMoreThanOnce() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
int initialNumberOfPostProcessors = applicationContext.getBeanFactoryPostProcessors().size();
for (int i = 0; i < 10; i++) {
new TestcontainersLifecycleApplicationContextInitializer().initialize(applicationContext);
}
assertThat(applicationContext.getBeanFactoryPostProcessors()).hasSize(initialNumberOfPostProcessors + 1);
}
private AnnotationConfigApplicationContext createApplicationContext(Startable container) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
new TestcontainersLifecycleApplicationContextInitializer().initialize(applicationContext);

View File

@ -24,8 +24,13 @@ import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ServiceConnectionContextCustomizerFactory}.
@ -39,8 +44,14 @@ class ServiceConnectionContextCustomizerFactoryTests {
private final ServiceConnectionContextCustomizerFactory factory = new ServiceConnectionContextCustomizerFactory();
@Test
void createContextCustomizerWhenNoServiceConnectionsReturnsNull() {
assertThat(this.factory.createContextCustomizer(NoServiceConnections.class, null)).isNull();
void createContextCustomizerWhenNoServiceConnectionsReturnsCustomizerToApplyInitializer() {
ContextCustomizer customizer = this.factory.createContextCustomizer(NoServiceConnections.class, null);
assertThat(customizer).isNotNull();
GenericApplicationContext context = new GenericApplicationContext();
int initialNumberOfPostProcessors = context.getBeanFactoryPostProcessors().size();
MergedContextConfiguration mergedConfig = mock(MergedContextConfiguration.class);
customizer.customizeContext(context, mergedConfig);
assertThat(context.getBeanFactoryPostProcessors()).hasSize(initialNumberOfPostProcessors + 1);
}
@Test