Skip to content
This repository was archived by the owner on Feb 23, 2023. It is now read-only.

Commit 8ba22ab

Browse files
ttddyysnicoll
authored andcommitted
Support "args" param in "@SpringBootTest"
Retrieve `args` parameter from `@SpringBootTest` at build time and pass it to the `AotSpringBootConfigContextLoader` for runtime use. See gh-1447
1 parent 8ddb376 commit 8ba22ab

File tree

5 files changed

+149
-16
lines changed

5 files changed

+149
-16
lines changed

spring-aot-test/src/main/java/org/springframework/aot/test/boot/SpringBootAotTestContextProcessor.java

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2021 the original author or authors.
2+
* Copyright 2019-2022 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.
@@ -16,15 +16,19 @@
1616

1717
package org.springframework.aot.test.boot;
1818

19+
import java.util.Arrays;
20+
1921
import com.squareup.javapoet.ClassName;
2022
import com.squareup.javapoet.CodeBlock;
2123
import com.squareup.javapoet.CodeBlock.Builder;
2224

25+
import org.springframework.aot.context.bootstrap.generator.bean.support.MultiCodeBlock;
2326
import org.springframework.aot.test.context.bootstrap.generator.AotTestContextProcessor;
2427
import org.springframework.boot.WebApplicationType;
2528
import org.springframework.boot.test.context.ReactiveWebMergedContextConfiguration;
2629
import org.springframework.boot.test.context.SpringBootTest;
2730
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
31+
import org.springframework.boot.test.context.SpringBootTestArgsAccessor;
2832
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
2933
import org.springframework.context.support.GenericApplicationContext;
3034
import org.springframework.core.annotation.MergedAnnotations;
@@ -62,16 +66,22 @@ public GenericApplicationContext prepareTestContext(MergedContextConfiguration c
6266

6367
@Override
6468
public CodeBlock writeInstanceSupplier(MergedContextConfiguration config, ClassName applicationContextInitializer) {
69+
String[] args = SpringBootTestArgsAccessor.get(config.getContextCustomizers());
70+
6571
Builder code = CodeBlock.builder();
6672
code.add("() -> new $T($T.class", AotSpringBootConfigContextLoader.class, applicationContextInitializer);
6773
WebApplicationType webApplicationType = detectWebApplicationType(config);
68-
if (webApplicationType.equals(WebApplicationType.NONE)) {
69-
code.add(")");
70-
}
71-
else {
72-
code.add(", $T.$L, $T.$L)", WebApplicationType.class, webApplicationType,
74+
if (!webApplicationType.equals(WebApplicationType.NONE)) {
75+
code.add(", $T.$L, $T.$L", WebApplicationType.class, webApplicationType,
7376
WebEnvironment.class, detectWebEnvironment(config));
7477
}
78+
if (args.length > 0) {
79+
MultiCodeBlock multi = new MultiCodeBlock();
80+
Arrays.stream(args).forEach((arg) -> multi.add("$S", arg));
81+
code.add(", $L", multi.join(", "));
82+
}
83+
code.add(")");
84+
7585
return code.build();
7686
}
7787

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2019-2022 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;
18+
19+
import java.util.Set;
20+
21+
import org.springframework.test.context.ContextCustomizer;
22+
23+
/**
24+
* Provide access to the package private {@link SpringBootTestArgs} class.
25+
*
26+
* @author Tadaya Tsuyukubo
27+
*/
28+
public class SpringBootTestArgsAccessor {
29+
30+
public static String[] get(Set<ContextCustomizer> customizers) {
31+
return SpringBootTestArgs.get(customizers);
32+
}
33+
34+
public static ContextCustomizer create(Class<?> testClass) {
35+
return new SpringBootTestArgs(testClass);
36+
}
37+
38+
}

spring-aot-test/src/test/java/org/springframework/aot/test/boot/SpringBootAotTestContextProcessorTests.java

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2021 the original author or authors.
2+
* Copyright 2019-2022 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.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.aot.test.boot;
1818

19+
import java.util.Set;
20+
1921
import com.squareup.javapoet.ClassName;
2022
import org.junit.jupiter.api.Test;
2123

@@ -25,8 +27,11 @@
2527
import org.springframework.aot.test.samples.app.slice.SampleJdbcTests;
2628
import org.springframework.boot.WebApplicationType;
2729
import org.springframework.boot.test.context.ReactiveWebMergedContextConfiguration;
30+
import org.springframework.boot.test.context.SpringBootTest;
31+
import org.springframework.boot.test.context.SpringBootTestArgsAccessor;
2832
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
2933
import org.springframework.context.support.GenericApplicationContext;
34+
import org.springframework.test.context.ContextCustomizer;
3035
import org.springframework.test.context.MergedContextConfiguration;
3136
import org.springframework.test.context.TestContextBootstrapper;
3237
import org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate;
@@ -130,10 +135,37 @@ void writeInstanceSupplierForReactiveWebSpringBootTest() {
130135
.isEqualTo("() -> new AotSpringBootConfigContextLoader(com.example.Test.class, WebApplicationType.REACTIVE, SpringBootTest.WebEnvironment.MOCK)");
131136
}
132137

138+
@Test
139+
void writeInstanceSupplierForNonWebWithArguments() {
140+
Set<ContextCustomizer> customizers = Set.of(SpringBootTestArgsAccessor.create(SampleApplicationWithArgumentsTests.class));
141+
MergedContextConfiguration contextConfiguration = mock(MergedContextConfiguration.class);
142+
given(contextConfiguration.getTestClass()).willAnswer((context) -> SampleApplicationTests.class);
143+
given(contextConfiguration.getContextCustomizers()).willAnswer((context) -> customizers);
144+
assertThat(CodeSnippet.of(this.processor.writeInstanceSupplier(contextConfiguration, ClassName.get("com.example", "Test"))))
145+
.hasImport(AotSpringBootConfigContextLoader.class)
146+
.isEqualTo("() -> new AotSpringBootConfigContextLoader(com.example.Test.class, \"--app.test=one\", \"--app.name=foo\")");
147+
}
148+
149+
@Test
150+
void writeInstanceSupplierForServletWithArguments() {
151+
Set<ContextCustomizer> customizers = Set.of(SpringBootTestArgsAccessor.create(SampleApplicationWithArgumentsTests.class));
152+
WebMergedContextConfiguration contextConfiguration = mock(WebMergedContextConfiguration.class);
153+
given(contextConfiguration.getTestClass()).willAnswer((context) -> SampleApplicationTests.class);
154+
given(contextConfiguration.getContextCustomizers()).willAnswer((context) -> customizers);
155+
assertThat(CodeSnippet.of(this.processor.writeInstanceSupplier(contextConfiguration, ClassName.get("com.example", "Test"))))
156+
.hasImport(AotSpringBootConfigContextLoader.class).hasImport(WebApplicationType.class)
157+
.isEqualTo("() -> new AotSpringBootConfigContextLoader(com.example.Test.class, WebApplicationType.SERVLET, SpringBootTest.WebEnvironment.MOCK, \"--app.test=one\", \"--app.name=foo\")");
158+
}
159+
133160
private TestContextBootstrapper createSpringBootTestContextBootstrapper(Class<?> testClass) {
134161
SpringBootTestContextBootstrapper bootstrapper = new SpringBootTestContextBootstrapper();
135162
bootstrapper.setBootstrapContext(new DefaultBootstrapContext(testClass, new DefaultCacheAwareContextLoaderDelegate()));
136163
return bootstrapper;
137164
}
138165

166+
@SpringBootTest(args = { "--app.test=one", "--app.name=foo" })
167+
static class SampleApplicationWithArgumentsTests {
168+
169+
}
170+
139171
}

spring-native/src/main/java/org/springframework/aot/test/boot/AotSpringBootConfigContextLoader.java

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2021 the original author or authors.
2+
* Copyright 2019-2022 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.
@@ -55,34 +55,36 @@ public class AotSpringBootConfigContextLoader extends SpringBootContextLoader {
5555

5656
private final WebEnvironment webEnvironment;
5757

58+
private final String[] args;
59+
5860
/**
5961
* Create an instance for the specified {@link ApplicationContextInitializer} and
6062
* web-related details.
6163
* @param testContextInitializer the context initializer to use
6264
* @param webApplicationType the {@link WebApplicationType} to use for the context
6365
* @param webEnvironment the {@link WebEnvironment} to use for the context
66+
* @param args the command line arguments
6467
*/
6568
public AotSpringBootConfigContextLoader(Class<? extends ApplicationContextInitializer<?>> testContextInitializer,
66-
WebApplicationType webApplicationType, WebEnvironment webEnvironment) {
69+
WebApplicationType webApplicationType, WebEnvironment webEnvironment, String... args) {
6770
this.testContextInitializer = testContextInitializer;
6871
this.webApplicationType = webApplicationType;
6972
this.webEnvironment = webEnvironment;
73+
this.args = args;
7074
}
7175

7276
/**
7377
* Create a new instance using the specified {@link ApplicationContextInitializer} for
7478
* a non-web context.
7579
* @param testContextInitializer the context initializer to use
80+
* @param args the command line arguments
7681
*/
77-
public AotSpringBootConfigContextLoader(Class<? extends ApplicationContextInitializer<?>> testContextInitializer) {
78-
this(testContextInitializer, WebApplicationType.NONE, WebEnvironment.NONE);
82+
public AotSpringBootConfigContextLoader(Class<? extends ApplicationContextInitializer<?>> testContextInitializer, String... args) {
83+
this(testContextInitializer, WebApplicationType.NONE, WebEnvironment.NONE, args);
7984
}
8085

8186
@Override
8287
public ConfigurableApplicationContext loadContext(MergedContextConfiguration config) {
83-
// TODO: handle application arguments
84-
String[] args = new String[0];
85-
8688
SpringApplication application = new AotTestSpringApplication(config.getTestClass().getClassLoader(), testContextInitializer);
8789
application.setMainApplicationClass(config.getTestClass());
8890
application.setSources(Collections.singleton(testContextInitializer.getName()));
@@ -108,7 +110,7 @@ else if (this.webApplicationType == WebApplicationType.REACTIVE) {
108110
ApplicationContextFactory.of(GenericReactiveWebApplicationContext::new));
109111
}
110112
}
111-
ConfigurableApplicationContext context = application.run(args);
113+
ConfigurableApplicationContext context = application.run(this.args);
112114

113115
return context;
114116
}

spring-native/src/test/java/org/springframework/aot/test/boot/AotSpringBootConfigContextLoaderTests.java

+52-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2021 the original author or authors.
2+
* Copyright 2019-2022 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.
@@ -16,12 +16,23 @@
1616

1717
package org.springframework.aot.test.boot;
1818

19+
import java.util.Collections;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.Map.Entry;
23+
import java.util.Set;
1924
import java.util.function.Consumer;
2025
import java.util.function.Supplier;
26+
import java.util.stream.Collectors;
27+
import java.util.stream.Stream;
2128

2229
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.params.ParameterizedTest;
31+
import org.junit.jupiter.params.provider.Arguments;
32+
import org.junit.jupiter.params.provider.MethodSource;
2333

2434
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
35+
import org.springframework.boot.ApplicationArguments;
2536
import org.springframework.boot.SpringBootConfiguration;
2637
import org.springframework.boot.WebApplicationType;
2738
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@@ -84,6 +95,38 @@ void loadContextSetActiveProfiles() {
8495
assertThat(context.getEnvironment().getActiveProfiles()).containsOnly("profile1", "profile2"));
8596
}
8697

98+
@ParameterizedTest
99+
@MethodSource("applicationArguments")
100+
void loadContextUsesArguments(String[] arguments, List<Entry<String, String>> expectedEntries) {
101+
AotSpringBootConfigContextLoader loader = new AotSpringBootConfigContextLoader(TestApplicationContextInitializer.class, arguments);
102+
run(() -> loader.loadContext(createMergedContextConfiguration(SampleTest.class)), (context) -> {
103+
ApplicationArguments args = context.getBean(ApplicationArguments.class);
104+
Set<String> keys = expectedEntries.stream().map(Entry::getKey).collect(Collectors.toSet());
105+
assertThat(args.getOptionNames()).containsExactlyElementsOf(keys);
106+
for (Entry<String, String> expectedEntry : expectedEntries) {
107+
String key = expectedEntry.getKey();
108+
String value = expectedEntry.getValue();
109+
assertThat(args.getOptionValues(key)).containsOnly(value);
110+
}
111+
});
112+
}
113+
114+
@ParameterizedTest
115+
@MethodSource("applicationArguments")
116+
void loadContextUsesArgumentsAndWebSettings(String[] arguments, List<Entry<String, String>> expectedEntries) {
117+
AotSpringBootConfigContextLoader loader = new AotSpringBootConfigContextLoader(TestApplicationContextInitializer.class, WebApplicationType.SERVLET, WebEnvironment.MOCK, arguments);
118+
run(() -> loader.loadContext(createMergedContextConfiguration(SampleTest.class)), (context) -> {
119+
ApplicationArguments args = context.getBean(ApplicationArguments.class);
120+
Set<String> keys = expectedEntries.stream().map(Entry::getKey).collect(Collectors.toSet());
121+
assertThat(args.getOptionNames()).containsExactlyElementsOf(keys);
122+
for (Entry<String, String> expectedEntry : expectedEntries) {
123+
String key = expectedEntry.getKey();
124+
String value = expectedEntry.getValue();
125+
assertThat(args.getOptionValues(key)).containsOnly(value);
126+
}
127+
});
128+
}
129+
87130
private void run(Supplier<ConfigurableApplicationContext> supplier, Consumer<AssertableApplicationContext> context) {
88131
try (ConfigurableApplicationContext ctx = supplier.get()) {
89132
context.accept(AssertableApplicationContext.get(() -> ctx));
@@ -125,4 +168,12 @@ public void initialize(GenericApplicationContext applicationContext) {
125168

126169
}
127170

171+
static Stream<Arguments> applicationArguments() {
172+
return Stream.of(
173+
Arguments.of(new String[] {}, Collections.emptyList()),
174+
Arguments.of(new String[] { "--app.test=one"}, List.of(Map.entry("app.test", "one"))),
175+
Arguments.of(new String[] { "--app.test=one", "--app.name=foo" }, List.of(Map.entry("app.test", "one"), Map.entry("app.name", "foo")))
176+
);
177+
}
178+
128179
}

0 commit comments

Comments
 (0)