Skip to content

Commit a7b4f77

Browse files
committed
GH-360 - Fix potential UnsupportedOperationException in test bootstrap.
See GH-345 and GH-356 for details.
1 parent b501554 commit a7b4f77

File tree

2 files changed

+54
-37
lines changed

2 files changed

+54
-37
lines changed

spring-modulith-test/src/main/java/org/springframework/modulith/test/ModuleTestAutoConfiguration.java

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,20 @@
1515
*/
1616
package org.springframework.modulith.test;
1717

18+
import java.util.ArrayList;
1819
import java.util.Arrays;
19-
import java.util.HashSet;
2020
import java.util.List;
21-
import java.util.Set;
2221

2322
import org.slf4j.Logger;
2423
import org.slf4j.LoggerFactory;
2524
import org.springframework.beans.factory.BeanFactory;
26-
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
2725
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2826
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
2927
import org.springframework.context.annotation.Configuration;
3028
import org.springframework.context.annotation.Import;
3129
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
3230
import org.springframework.core.Ordered;
3331
import org.springframework.core.type.AnnotationMetadata;
34-
import org.springframework.util.ReflectionUtils;
3532
import org.springframework.util.StringUtils;
3633

3734
/**
@@ -66,49 +63,25 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B
6663
LOGGER.info("Re-configuring auto-configuration and entity scan packages to: {}.",
6764
StringUtils.collectionToDelimitedString(basePackages, ", "));
6865

69-
setBasePackagesOn(registry, AUTOCONFIG_PACKAGES, "BasePackagesBeanDefinition", "basePackages", basePackages);
70-
setBasePackagesOn(registry, ENTITY_SCAN_PACKAGE, "EntityScanPackagesBeanDefinition", "packageNames",
71-
basePackages);
66+
setBasePackagesOn(registry, AUTOCONFIG_PACKAGES, basePackages);
67+
setBasePackagesOn(registry, ENTITY_SCAN_PACKAGE, basePackages);
7268
}
7369

74-
@SuppressWarnings("unchecked")
75-
private void setBasePackagesOn(BeanDefinitionRegistry registry, String beanName, String definitionType,
76-
String fieldName, List<String> packages) {
70+
private void setBasePackagesOn(BeanDefinitionRegistry registry, String beanName, List<String> packages) {
7771

7872
if (!registry.containsBeanDefinition(beanName)) {
7973
return;
8074
}
8175

76+
var packagesToSet = new ArrayList<>(packages);
8277
var definition = registry.getBeanDefinition(beanName);
78+
var holder = definition.getConstructorArgumentValues().getArgumentValue(0, String[].class);
8379

84-
// For Boot 2.4, we deal with a BasePackagesBeanDefinition
85-
var field = Arrays.stream(definition.getClass().getDeclaredFields())
86-
.filter(__ -> definition.getClass().getSimpleName().equals(definitionType))
87-
.filter(it -> it.getName().equals(fieldName))
88-
.findFirst()
89-
.orElse(null);
80+
Arrays.stream((String[]) holder.getValue())
81+
.filter(it -> it.startsWith("org.springframework.modulith"))
82+
.forEach(packagesToSet::add);
9083

91-
if (field != null) {
92-
93-
// Keep all auto-configuration packages from Moduliths
94-
95-
ReflectionUtils.makeAccessible(field);
96-
((Set<String>) ReflectionUtils.getField(field, definition)).stream()
97-
.filter(it -> it.startsWith("org.springframework.modulith"))
98-
.forEach(packages::add);
99-
100-
ReflectionUtils.setField(field, definition, new HashSet<>(packages));
101-
102-
} else {
103-
104-
ValueHolder holder = definition.getConstructorArgumentValues().getArgumentValue(0, String[].class);
105-
Arrays.stream((String[]) holder.getValue())
106-
.filter(it -> it.startsWith("org.springframework.modulith"))
107-
.forEach(packages::add);
108-
109-
// Fall back to customize the bean definition in a Boot 2.3 arrangement
110-
definition.getConstructorArgumentValues().addIndexedArgumentValue(0, packages.toArray(String[]::new));
111-
}
84+
definition.getConstructorArgumentValues().addIndexedArgumentValue(0, packagesToSet.toArray(String[]::new));
11285
}
11386
}
11487
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.springframework.modulith.test;
2+
3+
import static org.assertj.core.api.Assertions.*;
4+
import static org.mockito.Mockito.*;
5+
6+
import java.util.stream.Stream;
7+
8+
import org.junit.jupiter.api.Test;
9+
import org.springframework.boot.autoconfigure.AutoConfigurations;
10+
import org.springframework.boot.autoconfigure.domain.EntityScan;
11+
import org.springframework.boot.autoconfigure.domain.EntityScanPackages;
12+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
13+
14+
/**
15+
* Integration tests for {@link ModuleTestAutoConfiguration}.
16+
*
17+
* @author Oliver Drotbohm
18+
*/
19+
class ModuleTestAutoConfigurationIntegrationTests {
20+
21+
@Test // GH-360
22+
void bootstrapsTestWithManualEntityScanOfModulithPackage() {
23+
24+
var execution = mock(ModuleTestExecution.class);
25+
26+
doReturn(Stream.of("org.springframework.modulith.test"))
27+
.when(execution).getBasePackages();
28+
29+
new ApplicationContextRunner()
30+
.withBean(ModuleTestExecution.class, () -> execution)
31+
.withConfiguration(AutoConfigurations.of(ModuleTestAutoConfiguration.class))
32+
.withUserConfiguration(ManualModulithEntityScan.class)
33+
.run(ctx -> {
34+
35+
assertThat(ctx).hasNotFailed();
36+
assertThat(ctx.getBean(EntityScanPackages.class).getPackageNames())
37+
.containsExactlyInAnyOrder("org.springframework.modulith.test",
38+
"org.springframework.modulith.events.jpa");
39+
});
40+
}
41+
42+
@EntityScan("org.springframework.modulith.events.jpa")
43+
static class ManualModulithEntityScan {}
44+
}

0 commit comments

Comments
 (0)