1818
1919import java .lang .annotation .Retention ;
2020import java .lang .annotation .RetentionPolicy ;
21+ import java .util .function .BiConsumer ;
2122
2223import org .junit .jupiter .api .AfterEach ;
2324import org .junit .jupiter .api .Test ;
2425import org .testcontainers .containers .Container ;
2526import org .testcontainers .containers .PostgreSQLContainer ;
2627
28+ import org .springframework .aot .test .generate .TestGenerationContext ;
2729import org .springframework .boot .testcontainers .beans .TestcontainerBeanDefinition ;
2830import org .springframework .boot .testcontainers .context .ImportTestcontainers ;
31+ import org .springframework .boot .testcontainers .lifecycle .TestcontainersLifecycleApplicationContextInitializer ;
2932import org .springframework .boot .testsupport .container .DisabledIfDockerUnavailable ;
3033import org .springframework .boot .testsupport .container .TestImage ;
34+ import org .springframework .context .ApplicationContextInitializer ;
3135import org .springframework .context .annotation .AnnotationConfigApplicationContext ;
36+ import org .springframework .context .aot .ApplicationContextAotGenerator ;
37+ import org .springframework .context .support .GenericApplicationContext ;
38+ import org .springframework .core .test .tools .CompileWithForkedClassLoader ;
39+ import org .springframework .core .test .tools .Compiled ;
40+ import org .springframework .core .test .tools .TestCompiler ;
41+ import org .springframework .javapoet .ClassName ;
3242import org .springframework .test .context .DynamicPropertyRegistry ;
3343import org .springframework .test .context .DynamicPropertySource ;
3444
4353@ DisabledIfDockerUnavailable
4454class ImportTestcontainersTests {
4555
56+ private final TestGenerationContext generationContext = new TestGenerationContext ();
57+
4658 private AnnotationConfigApplicationContext applicationContext ;
4759
4860 @ AfterEach
@@ -102,7 +114,7 @@ void importWhenHasNonStaticContainerFieldThrowsException() {
102114 @ Test
103115 void importWhenHasContainerDefinitionsWithDynamicPropertySource () {
104116 this .applicationContext = new AnnotationConfigApplicationContext (
105- ContainerDefinitionsWithDynamicPropertySource .class );
117+ ImportWithoutValueWithDynamicPropertySource .class );
106118 assertThat (this .applicationContext .getEnvironment ().containsProperty ("container.port" )).isTrue ();
107119 }
108120
@@ -122,6 +134,100 @@ void importWhenHasBadArgsDynamicPropertySourceMethod() {
122134 .withMessage ("@DynamicPropertySource method 'containerProperties' must be static" );
123135 }
124136
137+ @ Test
138+ @ CompileWithForkedClassLoader
139+ void importTestcontainersImportWithoutValueAotContribution () {
140+ this .applicationContext = new AnnotationConfigApplicationContext ();
141+ this .applicationContext .register (ImportWithoutValue .class );
142+ compile ((freshContext , compiled ) -> {
143+ PostgreSQLContainer <?> container = freshContext .getBean (PostgreSQLContainer .class );
144+ assertThat (container ).isSameAs (ImportWithoutValue .container );
145+ });
146+ }
147+
148+ @ Test
149+ @ CompileWithForkedClassLoader
150+ void importTestcontainersImportWithValueAotContribution () {
151+ this .applicationContext = new AnnotationConfigApplicationContext ();
152+ this .applicationContext .register (ImportWithValue .class );
153+ compile ((freshContext , compiled ) -> {
154+ PostgreSQLContainer <?> container = freshContext .getBean (PostgreSQLContainer .class );
155+ assertThat (container ).isSameAs (ContainerDefinitions .container );
156+ });
157+ }
158+
159+ @ Test
160+ @ CompileWithForkedClassLoader
161+ void importTestcontainersImportWithoutValueWithDynamicPropertySourceAotContribution () {
162+ this .applicationContext = new AnnotationConfigApplicationContext ();
163+ this .applicationContext .register (ImportWithoutValueWithDynamicPropertySource .class );
164+ compile ((freshContext , compiled ) -> {
165+ PostgreSQLContainer <?> container = freshContext .getBean (PostgreSQLContainer .class );
166+ assertThat (container ).isSameAs (ImportWithoutValueWithDynamicPropertySource .container );
167+ assertThat (freshContext .getEnvironment ().getProperty ("container.port" , Integer .class ))
168+ .isEqualTo (ImportWithoutValueWithDynamicPropertySource .container .getFirstMappedPort ());
169+ });
170+ }
171+
172+ @ Test
173+ @ CompileWithForkedClassLoader
174+ void importTestcontainersCustomPostgreSQLContainerDefinitionsAotContribution () {
175+ this .applicationContext = new AnnotationConfigApplicationContext ();
176+ this .applicationContext .register (CustomPostgreSQLContainerDefinitions .class );
177+ compile ((freshContext , compiled ) -> {
178+ CustomPostgreSQLContainer container = freshContext .getBean (CustomPostgreSQLContainer .class );
179+ assertThat (container ).isSameAs (CustomPostgreSQLContainerDefinitions .container );
180+ });
181+ }
182+
183+ @ Test
184+ @ CompileWithForkedClassLoader
185+ void importTestcontainersImportWithoutValueNotAccessibleContainerAndDynamicPropertySourceAotContribution () {
186+ this .applicationContext = new AnnotationConfigApplicationContext ();
187+ this .applicationContext .register (ImportWithoutValueNotAccessibleContainerAndDynamicPropertySource .class );
188+ compile ((freshContext , compiled ) -> {
189+ PostgreSQLContainer <?> container = freshContext .getBean (PostgreSQLContainer .class );
190+ assertThat (container ).isSameAs (ImportWithoutValueNotAccessibleContainerAndDynamicPropertySource .container );
191+ assertThat (freshContext .getEnvironment ().getProperty ("container.port" , Integer .class )).isEqualTo (
192+ ImportWithoutValueNotAccessibleContainerAndDynamicPropertySource .container .getFirstMappedPort ());
193+ });
194+ }
195+
196+ @ Test
197+ @ CompileWithForkedClassLoader
198+ void importTestcontainersWithNotAccessibleContainerAndDynamicPropertySourceAotContribution () {
199+ this .applicationContext = new AnnotationConfigApplicationContext ();
200+ this .applicationContext .register (ImportWithValueAndDynamicPropertySource .class );
201+ compile ((freshContext , compiled ) -> {
202+ PostgreSQLContainer <?> container = freshContext .getBean (PostgreSQLContainer .class );
203+ assertThat (container ).isSameAs (ContainerDefinitionsWithDynamicPropertySource .container );
204+ assertThat (freshContext .getEnvironment ().getProperty ("container.port" , Integer .class ))
205+ .isEqualTo (ContainerDefinitionsWithDynamicPropertySource .container .getFirstMappedPort ());
206+ });
207+ }
208+
209+ @ SuppressWarnings ("unchecked" )
210+ private void compile (BiConsumer <GenericApplicationContext , Compiled > result ) {
211+ ClassName className = processAheadOfTime ();
212+ TestCompiler .forSystem ().with (this .generationContext ).compile ((compiled ) -> {
213+ try (GenericApplicationContext context = new GenericApplicationContext ()) {
214+ new TestcontainersLifecycleApplicationContextInitializer ().initialize (context );
215+ ApplicationContextInitializer <GenericApplicationContext > initializer = compiled
216+ .getInstance (ApplicationContextInitializer .class , className .toString ());
217+ initializer .initialize (context );
218+ context .refresh ();
219+ result .accept (context , compiled );
220+ }
221+ });
222+ }
223+
224+ private ClassName processAheadOfTime () {
225+ ClassName className = new ApplicationContextAotGenerator ().processAheadOfTime (this .applicationContext ,
226+ this .generationContext );
227+ this .generationContext .writeGeneratedContent ();
228+ return className ;
229+ }
230+
125231 @ ImportTestcontainers
126232 static class ImportWithoutValue {
127233
@@ -161,13 +267,25 @@ interface ContainerDefinitions {
161267
162268 }
163269
270+ private interface ContainerDefinitionsWithDynamicPropertySource {
271+
272+ @ ContainerAnnotation
273+ PostgreSQLContainer <?> container = TestImage .container (PostgreSQLContainer .class );
274+
275+ @ DynamicPropertySource
276+ static void containerProperties (DynamicPropertyRegistry registry ) {
277+ registry .add ("container.port" , container ::getFirstMappedPort );
278+ }
279+
280+ }
281+
164282 @ Retention (RetentionPolicy .RUNTIME )
165283 @interface ContainerAnnotation {
166284
167285 }
168286
169287 @ ImportTestcontainers
170- static class ContainerDefinitionsWithDynamicPropertySource {
288+ static class ImportWithoutValueWithDynamicPropertySource {
171289
172290 static PostgreSQLContainer <?> container = TestImage .container (PostgreSQLContainer .class );
173291
@@ -196,4 +314,36 @@ void containerProperties() {
196314
197315 }
198316
317+ @ ImportTestcontainers
318+ static class CustomPostgreSQLContainerDefinitions {
319+
320+ private static final CustomPostgreSQLContainer container = new CustomPostgreSQLContainer ();
321+
322+ }
323+
324+ static class CustomPostgreSQLContainer extends PostgreSQLContainer <CustomPostgreSQLContainer > {
325+
326+ CustomPostgreSQLContainer () {
327+ super ("postgres:14" );
328+ }
329+
330+ }
331+
332+ @ ImportTestcontainers
333+ static class ImportWithoutValueNotAccessibleContainerAndDynamicPropertySource {
334+
335+ private static final PostgreSQLContainer <?> container = TestImage .container (PostgreSQLContainer .class );
336+
337+ @ DynamicPropertySource
338+ private static void containerProperties (DynamicPropertyRegistry registry ) {
339+ registry .add ("container.port" , container ::getFirstMappedPort );
340+ }
341+
342+ }
343+
344+ @ ImportTestcontainers (ContainerDefinitionsWithDynamicPropertySource .class )
345+ static class ImportWithValueAndDynamicPropertySource {
346+
347+ }
348+
199349}
0 commit comments