Skip to content

Commit bebca55

Browse files
committed
Add testAndDevelopmentOnly configuration
Closes gh-35436
1 parent b52a781 commit bebca55

File tree

33 files changed

+587
-24
lines changed

33 files changed

+587
-24
lines changed

spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,7 @@ This is especially useful for Testcontainer `Container` beans, as they keep thei
11151115

11161116
include::code:MyContainersConfiguration[]
11171117

1118-
WARNING: If you're using Gradle and want to use this feature, you need to change the configuration of the `spring-boot-devtools` dependency from `developmentOnly` to `testImplementation`.
1118+
WARNING: If you're using Gradle and want to use this feature, you need to change the configuration of the `spring-boot-devtools` dependency from `developmentOnly` to `testAndDevelopmentOnly`.
11191119
With the default scope of `developmentOnly`, the `bootTestRun` task will not pick up changes in your code, as the devtools are not active.
11201120

11211121

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/reacting.adoc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ When Gradle's {java-plugin}[`java` plugin] is applied to a project, the Spring B
1818
6. Creates a {boot-run-javadoc}['BootRun`] task named `bootTestRun` that can be used to run your application using the `test` source set to find its main method and provide its runtime classpath.
1919
7. Creates a configuration named `bootArchives` that contains the artifact produced by the `bootJar` task.
2020
8. Creates a configuration named `developmentOnly` for dependencies that are only required at development time, such as Spring Boot's Devtools, and should not be packaged in executable jars and wars.
21-
9. Creates a configuration named `productionRuntimeClasspath`. It is equivalent to `runtimeClasspath` minus any dependencies that only appear in the `developmentOnly` configuration.
22-
10. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`.
23-
11. Configures any `JavaCompile` tasks to use the `-parameters` compiler argument.
21+
9. Creats a configuration named `testAndDevelopmentOnly` for dependencies that are only required at development time and when writing and running tests and that should not be packaged in executable jars and wars.
22+
10. Creates a configuration named `productionRuntimeClasspath`. It is equivalent to `runtimeClasspath` minus any dependencies that only appear in the `developmentOnly` or `testDevelopmentOnly` configurations.
23+
11. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`.
24+
12. Configures any `JavaCompile` tasks to use the `-parameters` compiler argument.
2425

2526

2627

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/JavaPluginAction.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ public Class<? extends Plugin<? extends Project>> getPluginClass() {
7878
public void execute(Project project) {
7979
classifyJarTask(project);
8080
configureBuildTask(project);
81+
configureProductionRuntimeClasspathConfiguration(project);
8182
configureDevelopmentOnlyConfiguration(project);
83+
configureTestAndDevelopmentOnlyConfiguration(project);
8284
TaskProvider<ResolveMainClassName> resolveMainClassName = configureResolveMainClassNameTask(project);
8385
TaskProvider<BootJar> bootJar = configureBootJarTask(project, resolveMainClassName);
8486
configureBootBuildImageTask(project, bootJar);
@@ -160,12 +162,15 @@ private TaskProvider<BootJar> configureBootJarTask(Project project,
160162
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
161163
Configuration developmentOnly = project.getConfigurations()
162164
.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
165+
Configuration testAndDevelopmentOnly = project.getConfigurations()
166+
.getByName(SpringBootPlugin.TEST_AND_DEVELOPMENT_ONLY_CONFIGURATION_NAME);
163167
Configuration productionRuntimeClasspath = project.getConfigurations()
164168
.getByName(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
165169
Configuration runtimeClasspath = project.getConfigurations()
166170
.getByName(mainSourceSet.getRuntimeClasspathConfigurationName());
167171
Callable<FileCollection> classpath = () -> mainSourceSet.getRuntimeClasspath()
168172
.minus((developmentOnly.minus(productionRuntimeClasspath)))
173+
.minus((testAndDevelopmentOnly.minus(productionRuntimeClasspath)))
169174
.filter(new JarTypeFileSpec());
170175
return project.getTasks().register(SpringBootPlugin.BOOT_JAR_TASK_NAME, BootJar.class, (bootJar) -> {
171176
bootJar.setDescription(
@@ -270,13 +275,7 @@ private void configureAdditionalMetadataLocations(JavaCompile compile) {
270275
.ifPresent((locations) -> compile.doFirst(new AdditionalMetadataLocationsConfigurer(locations)));
271276
}
272277

273-
private void configureDevelopmentOnlyConfiguration(Project project) {
274-
Configuration developmentOnly = project.getConfigurations()
275-
.create(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
276-
developmentOnly
277-
.setDescription("Configuration for development-only dependencies such as Spring Boot's DevTools.");
278-
Configuration runtimeClasspath = project.getConfigurations()
279-
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
278+
private void configureProductionRuntimeClasspathConfiguration(Project project) {
280279
Configuration productionRuntimeClasspath = project.getConfigurations()
281280
.create(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
282281
AttributeContainer attributes = productionRuntimeClasspath.getAttributes();
@@ -286,12 +285,37 @@ private void configureDevelopmentOnlyConfiguration(Project project) {
286285
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
287286
objectFactory.named(LibraryElements.class, LibraryElements.JAR));
288287
productionRuntimeClasspath.setVisible(false);
288+
Configuration runtimeClasspath = project.getConfigurations()
289+
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
289290
productionRuntimeClasspath.setExtendsFrom(runtimeClasspath.getExtendsFrom());
290291
productionRuntimeClasspath.setCanBeResolved(runtimeClasspath.isCanBeResolved());
291292
productionRuntimeClasspath.setCanBeConsumed(runtimeClasspath.isCanBeConsumed());
293+
}
294+
295+
private void configureDevelopmentOnlyConfiguration(Project project) {
296+
Configuration developmentOnly = project.getConfigurations()
297+
.create(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
298+
developmentOnly
299+
.setDescription("Configuration for development-only dependencies such as Spring Boot's DevTools.");
300+
Configuration runtimeClasspath = project.getConfigurations()
301+
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
302+
292303
runtimeClasspath.extendsFrom(developmentOnly);
293304
}
294305

306+
private void configureTestAndDevelopmentOnlyConfiguration(Project project) {
307+
Configuration testAndDevelopmentOnly = project.getConfigurations()
308+
.create(SpringBootPlugin.TEST_AND_DEVELOPMENT_ONLY_CONFIGURATION_NAME);
309+
testAndDevelopmentOnly
310+
.setDescription("Configuration for test and development-only dependencies such as Spring Boot's DevTools.");
311+
Configuration runtimeClasspath = project.getConfigurations()
312+
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
313+
runtimeClasspath.extendsFrom(testAndDevelopmentOnly);
314+
Configuration testImplementation = project.getConfigurations()
315+
.getByName(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME);
316+
testImplementation.extendsFrom(testAndDevelopmentOnly);
317+
}
318+
295319
/**
296320
* Task {@link Action} to add additional meta-data locations. We need to use an
297321
* inner-class rather than a lambda due to

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/NativeImagePluginAction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ private Iterable<Configuration> removeDevelopmentOnly(Set<Configuration> configu
8383
}
8484

8585
private boolean isNotDevelopmentOnly(Configuration configuration) {
86-
return !SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME.equals(configuration.getName());
86+
return !SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME.equals(configuration.getName())
87+
&& !SpringBootPlugin.TEST_AND_DEVELOPMENT_ONLY_CONFIGURATION_NAME.equals(configuration.getName());
8788
}
8889

8990
private void configureTestNativeBinaryClasspath(SourceSetContainer sourceSets, GraalVMExtension graalVmExtension) {

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootAotPlugin.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ private void configureJavaRuntimeUsageAttribute(Project project, AttributeContai
117117
private void registerProcessAotTask(Project project, SourceSet aotSourceSet, SourceSet mainSourceSet) {
118118
TaskProvider<ResolveMainClassName> resolveMainClassName = project.getTasks()
119119
.named(SpringBootPlugin.RESOLVE_MAIN_CLASS_NAME_TASK_NAME, ResolveMainClassName.class);
120-
Configuration aotClasspath = createAotProcessingClasspath(project, PROCESS_AOT_TASK_NAME, mainSourceSet);
120+
Configuration aotClasspath = createAotProcessingClasspath(project, PROCESS_AOT_TASK_NAME, mainSourceSet,
121+
Set.of(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME,
122+
SpringBootPlugin.TEST_AND_DEVELOPMENT_ONLY_CONFIGURATION_NAME));
121123
project.getDependencies().add(aotClasspath.getName(), project.files(mainSourceSet.getOutput()));
122124
Configuration compileClasspath = project.getConfigurations()
123125
.getByName(aotSourceSet.getCompileClasspathConfigurationName());
@@ -152,14 +154,16 @@ private void configureAotTask(Project project, SourceSet sourceSet, AbstractAot
152154
}
153155

154156
@SuppressWarnings({ "unchecked", "rawtypes" })
155-
private Configuration createAotProcessingClasspath(Project project, String taskName, SourceSet inputSourceSet) {
157+
private Configuration createAotProcessingClasspath(Project project, String taskName, SourceSet inputSourceSet,
158+
Set<String> developmentOnlyConfigurationNames) {
156159
Configuration base = project.getConfigurations()
157160
.getByName(inputSourceSet.getRuntimeClasspathConfigurationName());
158161
return project.getConfigurations().create(taskName + "Classpath", (classpath) -> {
159162
classpath.setCanBeConsumed(false);
160163
classpath.setCanBeResolved(true);
161164
classpath.setDescription("Classpath of the " + taskName + " task.");
162-
removeDevelopmentOnly(base.getExtendsFrom()).forEach(classpath::extendsFrom);
165+
removeDevelopmentOnly(base.getExtendsFrom(), developmentOnlyConfigurationNames)
166+
.forEach(classpath::extendsFrom);
163167
classpath.attributes((attributes) -> {
164168
ProviderFactory providers = project.getProviders();
165169
AttributeContainer baseAttributes = base.getAttributes();
@@ -171,12 +175,10 @@ private Configuration createAotProcessingClasspath(Project project, String taskN
171175
});
172176
}
173177

174-
private Stream<Configuration> removeDevelopmentOnly(Set<Configuration> configurations) {
175-
return configurations.stream().filter(this::isNotDevelopmentOnly);
176-
}
177-
178-
private boolean isNotDevelopmentOnly(Configuration configuration) {
179-
return !SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME.equals(configuration.getName());
178+
private Stream<Configuration> removeDevelopmentOnly(Set<Configuration> configurations,
179+
Set<String> developmentOnlyConfigurationNames) {
180+
return configurations.stream()
181+
.filter((configuration) -> !developmentOnlyConfigurationNames.contains(configuration.getName()));
180182
}
181183

182184
private void configureDependsOn(Project project, SourceSet aotSourceSet,
@@ -188,7 +190,8 @@ private void configureDependsOn(Project project, SourceSet aotSourceSet,
188190

189191
private void registerProcessTestAotTask(Project project, SourceSet mainSourceSet, SourceSet aotTestSourceSet,
190192
SourceSet testSourceSet) {
191-
Configuration aotClasspath = createAotProcessingClasspath(project, PROCESS_TEST_AOT_TASK_NAME, testSourceSet);
193+
Configuration aotClasspath = createAotProcessingClasspath(project, PROCESS_TEST_AOT_TASK_NAME, testSourceSet,
194+
Set.of(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME));
192195
addJUnitPlatformLauncherDependency(project, aotClasspath);
193196
Configuration compileClasspath = project.getConfigurations()
194197
.getByName(aotTestSourceSet.getCompileClasspathConfigurationName());

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ public class SpringBootPlugin implements Plugin<Project> {
8080
*/
8181
public static final String DEVELOPMENT_ONLY_CONFIGURATION_NAME = "developmentOnly";
8282

83+
/**
84+
* The name of the {@code testAndDevelopmentOnly} configuration.
85+
* @since 3.2.0
86+
*/
87+
public static final String TEST_AND_DEVELOPMENT_ONLY_CONFIGURATION_NAME = "testAndDevelopmentOnly";
88+
8389
/**
8490
* The name of the {@code productionRuntimeClasspath} configuration.
8591
*/

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/WarPluginAction.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ private void classifyWarTask(Project project) {
7272
private TaskProvider<BootWar> configureBootWarTask(Project project) {
7373
Configuration developmentOnly = project.getConfigurations()
7474
.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
75+
Configuration testAndDevelopmentOnly = project.getConfigurations()
76+
.getByName(SpringBootPlugin.TEST_AND_DEVELOPMENT_ONLY_CONFIGURATION_NAME);
7577
Configuration productionRuntimeClasspath = project.getConfigurations()
7678
.getByName(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
7779
SourceSet mainSourceSet = project.getExtensions()
@@ -82,6 +84,7 @@ private TaskProvider<BootWar> configureBootWarTask(Project project) {
8284
Callable<FileCollection> classpath = () -> mainSourceSet.getRuntimeClasspath()
8385
.minus(providedRuntimeConfiguration(project))
8486
.minus((developmentOnly.minus(productionRuntimeClasspath)))
87+
.minus((testAndDevelopmentOnly.minus(productionRuntimeClasspath)))
8588
.filter(new JarTypeFileSpec());
8689
TaskProvider<ResolveMainClassName> resolveMainClassName = project.getTasks()
8790
.named(SpringBootPlugin.RESOLVE_MAIN_CLASS_NAME_TASK_NAME, ResolveMainClassName.class);

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/JavaPluginActionIntegrationTests.java

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,52 @@ void additionalMetadataLocationsNotConfiguredWhenProcessorIsAbsent() throws IOEx
142142

143143
@TestTemplate
144144
void applyingJavaPluginCreatesDevelopmentOnlyConfiguration() {
145-
assertThat(this.gradleBuild.build("build").getOutput()).contains("developmentOnly exists = true");
145+
assertThat(this.gradleBuild.build("help").getOutput()).contains("developmentOnly exists = true");
146+
}
147+
148+
@TestTemplate
149+
void applyingJavaPluginCreatesTestAndDevelopmentOnlyConfiguration() {
150+
assertThat(this.gradleBuild.build("help").getOutput()).contains("testAndDevelopmentOnly exists = true");
151+
}
152+
153+
@TestTemplate
154+
void testCompileClasspathIncludesTestAndDevelopmentOnlyDependencies() {
155+
assertThat(this.gradleBuild.build("help").getOutput()).contains("commons-lang3-3.12.0.jar");
156+
}
157+
158+
@TestTemplate
159+
void testRuntimeClasspathIncludesTestAndDevelopmentOnlyDependencies() {
160+
assertThat(this.gradleBuild.build("help").getOutput()).contains("commons-lang3-3.12.0.jar");
161+
}
162+
163+
@TestTemplate
164+
void testCompileClasspathDoesNotIncludeDevelopmentOnlyDependencies() {
165+
assertThat(this.gradleBuild.build("help").getOutput()).doesNotContain("commons-lang3-3.12.0.jar");
166+
}
167+
168+
@TestTemplate
169+
void testRuntimeClasspathDoesNotIncludeDevelopmentOnlyDependencies() {
170+
assertThat(this.gradleBuild.build("help").getOutput()).doesNotContain("commons-lang3-3.12.0.jar");
171+
}
172+
173+
@TestTemplate
174+
void compileClasspathDoesNotIncludeTestAndDevelopmentOnlyDependencies() {
175+
assertThat(this.gradleBuild.build("help").getOutput()).doesNotContain("commons-lang3-3.12.0.jar");
176+
}
177+
178+
@TestTemplate
179+
void runtimeClasspathIncludesTestAndDevelopmentOnlyDependencies() {
180+
assertThat(this.gradleBuild.build("help").getOutput()).contains("commons-lang3-3.12.0.jar");
181+
}
182+
183+
@TestTemplate
184+
void compileClasspathDoesNotIncludeDevelopmentOnlyDependencies() {
185+
assertThat(this.gradleBuild.build("help").getOutput()).doesNotContain("commons-lang3-3.12.0.jar");
186+
}
187+
188+
@TestTemplate
189+
void runtimeClasspathIncludesDevelopmentOnlyDependencies() {
190+
assertThat(this.gradleBuild.build("help").getOutput()).contains("commons-lang3-3.12.0.jar");
146191
}
147192

148193
@TestTemplate

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/NativeImagePluginActionIntegrationTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ void developmentOnlyDependenciesDoNotAppearInNativeImageClasspath() {
105105
assertThat(result.getOutput()).doesNotContain("commons-lang");
106106
}
107107

108+
@TestTemplate
109+
void testAndDevelopmentOnlyDependenciesDoNotAppearInNativeImageClasspath() {
110+
writeDummySpringApplicationAotProcessorMainClass();
111+
BuildResult result = this.gradleBuild.expectDeprecationWarningsWithAtLeastVersion("8.2-rc-1")
112+
.build("checkNativeImageClasspath");
113+
assertThat(result.getOutput()).doesNotContain("commons-lang");
114+
}
115+
108116
@TestTemplate
109117
void classesGeneratedDuringAotProcessingAreOnTheNativeImageClasspath() {
110118
BuildResult result = this.gradleBuild.expectDeprecationWarningsWithAtLeastVersion("8.2-rc-1")

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/SpringBootAotPluginIntegrationTests.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,22 @@ void processAotDoesNotHaveDevelopmentOnlyDependenciesOnItsClasspath() {
106106

107107
@TestTemplate
108108
void processTestAotDoesNotHaveDevelopmentOnlyDependenciesOnItsClasspath() {
109-
String output = this.gradleBuild.build("processTestAotClasspath", "--stacktrace").getOutput();
109+
String output = this.gradleBuild.build("processTestAotClasspath").getOutput();
110+
assertThat(output).doesNotContain("commons-lang");
111+
}
112+
113+
@TestTemplate
114+
void processAotDoesNotHaveTestAndDevelopmentOnlyDependenciesOnItsClasspath() {
115+
String output = this.gradleBuild.build("processAotClasspath").getOutput();
110116
assertThat(output).doesNotContain("commons-lang");
111117
}
112118

119+
@TestTemplate
120+
void processTestAotHasTestAndDevelopmentOnlyDependenciesOnItsClasspath() {
121+
String output = this.gradleBuild.build("processTestAotClasspath").getOutput();
122+
assertThat(output).contains("commons-lang");
123+
}
124+
113125
@TestTemplate
114126
void processAotRunsWhenProjectHasMainSource() throws IOException {
115127
writeMainClass("org.springframework.boot", "SpringApplicationAotProcessor");

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveIntegrationTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,41 @@ void developmentOnlyDependenciesCanBeIncludedInTheArchive() throws IOException {
221221
}
222222
}
223223

224+
@TestTemplate
225+
void testAndDevelopmentOnlyDependenciesAreNotIncludedInTheArchiveByDefault() throws IOException {
226+
File srcMainResources = new File(this.gradleBuild.getProjectDir(), "src/main/resources");
227+
srcMainResources.mkdirs();
228+
new File(srcMainResources, "resource").createNewFile();
229+
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
230+
.isEqualTo(TaskOutcome.SUCCESS);
231+
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
232+
Stream<String> libEntryNames = jarFile.stream()
233+
.filter((entry) -> !entry.isDirectory())
234+
.map(JarEntry::getName)
235+
.filter((name) -> name.startsWith(this.libPath));
236+
assertThat(libEntryNames).containsExactly(this.libPath + "commons-io-2.6.jar");
237+
Stream<String> classesEntryNames = jarFile.stream()
238+
.filter((entry) -> !entry.isDirectory())
239+
.map(JarEntry::getName)
240+
.filter((name) -> name.startsWith(this.classesPath));
241+
assertThat(classesEntryNames).containsExactly(this.classesPath + "resource");
242+
}
243+
}
244+
245+
@TestTemplate
246+
void testAndDevelopmentOnlyDependenciesCanBeIncludedInTheArchive() throws IOException {
247+
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
248+
.isEqualTo(TaskOutcome.SUCCESS);
249+
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
250+
Stream<String> libEntryNames = jarFile.stream()
251+
.filter((entry) -> !entry.isDirectory())
252+
.map(JarEntry::getName)
253+
.filter((name) -> name.startsWith(this.libPath));
254+
assertThat(libEntryNames).containsExactly(this.libPath + "commons-io-2.6.jar",
255+
this.libPath + "commons-lang3-3.9.jar");
256+
}
257+
}
258+
224259
@TestTemplate
225260
void jarTypeFilteringIsApplied() throws IOException {
226261
File flatDirRepository = new File(this.gradleBuild.getProjectDir(), "repository");

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,22 @@ void classesFromASecondarySourceSetCanBeOnTheClasspath() throws IOException {
146146
assertThat(result.getOutput()).contains("com.example.bootrun.main.CustomMainClass");
147147
}
148148

149+
@TestTemplate
150+
void developmentOnlyDependenciesAreOnTheClasspath() throws IOException {
151+
copyClasspathApplication();
152+
BuildResult result = this.gradleBuild.build("bootRun");
153+
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
154+
assertThat(result.getOutput()).contains("commons-lang3-3.12.0.jar");
155+
}
156+
157+
@TestTemplate
158+
void testAndDevelopmentOnlyDependenciesAreOnTheClasspath() throws IOException {
159+
copyClasspathApplication();
160+
BuildResult result = this.gradleBuild.build("bootRun");
161+
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
162+
assertThat(result.getOutput()).contains("commons-lang3-3.12.0.jar");
163+
}
164+
149165
private void copyMainClassApplication() throws IOException {
150166
copyApplication("main");
151167
}

0 commit comments

Comments
 (0)