Skip to content

Commit

Permalink
Import testcontainers configuration in integration tests
Browse files Browse the repository at this point in the history
This changes the code generation of the Testcontainers support: the
testcontainer beans are now defined in their own configuration. This
configuration is used in the test main method, and is @Import-ed in
the integration test.
The result is that the test main method starts testcontainers, and the
tests pass too as they now also start the testcontainers.

Closes gh-1247
  • Loading branch information
mhalbritter committed Jun 6, 2024
1 parent 29980bc commit 4cadd87
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,14 +52,15 @@ class GroovyTestContainersApplicationCodeProjectContributor extends

@Override
protected void contributeCode(GroovySourceCode sourceCode) {
super.contributeCode(sourceCode);
customizeApplicationTypeDeclaration(sourceCode, (type) -> {
type.modifiers(Modifier.PUBLIC);
type.addMethodDeclaration(GroovyMethodDeclaration.method("main")
.modifiers(Modifier.PUBLIC | Modifier.STATIC)
.returning("void")
.parameters(Parameter.of("args", String[].class))
.body(CodeBlock.ofStatement("$T.from($L::main).with($L).run(args)", SpringApplication.class,
getDescription().getApplicationName(), getTestApplicationName())));
.body(CodeBlock.ofStatement("$T.from($L::main).with($T).run(args)", SpringApplication.class,
getDescription().getApplicationName(), TESTCONTAINERS_CONFIGURATION_CLASS_NAME)));
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.testcontainers;

import io.spring.initializr.generator.language.ClassName;
import io.spring.initializr.generator.language.TypeDeclaration;
import io.spring.initializr.generator.spring.code.TestApplicationTypeCustomizer;

/**
* {@link TestApplicationTypeCustomizer} that imports the generated Testcontainers test
* configuration.
*
* @author Moritz Halbritter
*/
class ImportTestcontainersConfigurationTestApplicationTypeCustomizer
implements TestApplicationTypeCustomizer<TypeDeclaration> {

@Override
public void customize(TypeDeclaration typeDeclaration) {
typeDeclaration.annotations()
.add(ClassName.of("org.springframework.context.annotation.Import"), (annotation) -> annotation.set("value",
TestContainersApplicationCodeProjectContributor.TESTCONTAINERS_CONFIGURATION_CLASS_NAME));
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,14 +52,15 @@ class JavaTestContainersApplicationCodeProjectContributor extends

@Override
protected void contributeCode(JavaSourceCode sourceCode) {
super.contributeCode(sourceCode);
customizeApplicationTypeDeclaration(sourceCode, (type) -> {
type.modifiers(Modifier.PUBLIC);
type.addMethodDeclaration(JavaMethodDeclaration.method("main")
.modifiers(Modifier.PUBLIC | Modifier.STATIC)
.returning("void")
.parameters(Parameter.of("args", String[].class))
.body(CodeBlock.ofStatement("$T.from($L::main).with($L.class).run(args)", SpringApplication.class,
getDescription().getApplicationName(), getTestApplicationName())));
.body(CodeBlock.ofStatement("$T.from($L::main).with($T.class).run(args)", SpringApplication.class,
getDescription().getApplicationName(), TESTCONTAINERS_CONFIGURATION_CLASS_NAME)));
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,15 +17,13 @@
package io.spring.start.site.extension.dependency.testcontainers;

import java.util.Arrays;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.language.CodeBlock;
import io.spring.initializr.generator.language.Parameter;
import io.spring.initializr.generator.language.kotlin.KotlinCompilationUnit;
import io.spring.initializr.generator.language.kotlin.KotlinFunctionDeclaration;
import io.spring.initializr.generator.language.kotlin.KotlinModifier;
import io.spring.initializr.generator.language.kotlin.KotlinSourceCode;
import io.spring.initializr.generator.language.kotlin.KotlinSourceCodeWriter;
import io.spring.initializr.generator.language.kotlin.KotlinTypeDeclaration;
Expand Down Expand Up @@ -53,18 +51,13 @@ class KotlinTestContainersApplicationCodeProjectContributor extends

@Override
protected void contributeCode(KotlinSourceCode sourceCode) {
customizeApplicationTypeDeclaration(sourceCode, (type) -> type.modifiers(KotlinModifier.PUBLIC));
}

@Override
protected void customizeApplicationCompilationUnit(KotlinSourceCode sourceCode,
Consumer<KotlinCompilationUnit> customizer) {
super.customizeApplicationCompilationUnit(sourceCode, customizer
.andThen((compilationUnit) -> compilationUnit.addTopLevelFunction(KotlinFunctionDeclaration.function("main")
.parameters(Parameter.of("args", "Array<String>"))
.body(CodeBlock.ofStatement("$T<$L>().$T($L::class).run(*args)",
"org.springframework.boot.fromApplication", getDescription().getApplicationName(),
"org.springframework.boot.with", getTestApplicationName())))));
super.contributeCode(sourceCode);
customizeApplicationCompilationUnit(sourceCode,
(compilationUnit) -> compilationUnit.addTopLevelFunction(KotlinFunctionDeclaration.function("main")
.parameters(Parameter.of("args", "Array<String>"))
.body(CodeBlock.ofStatement("$T<$L>().$T($T::class).run(*args)",
"org.springframework.boot.fromApplication", getDescription().getApplicationName(),
"org.springframework.boot.with", TESTCONTAINERS_CONFIGURATION_CLASS_NAME))));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -40,6 +40,7 @@
* @param <C> language-specific compilation unit
* @param <S> language-specific source code
* @author Stephane Nicoll
* @author Moritz Halbritter
*/
abstract class TestContainersApplicationCodeProjectContributor<T extends TypeDeclaration, C extends CompilationUnit<T>, S extends SourceCode<T, C>>
implements ProjectContributor {
Expand All @@ -55,6 +56,8 @@ abstract class TestContainersApplicationCodeProjectContributor<T extends TypeDec
private static final ClassName DOCKER_IMAGE_NAME_CLASS_NAME = ClassName
.of("org.testcontainers.utility.DockerImageName");

static final ClassName TESTCONTAINERS_CONFIGURATION_CLASS_NAME = ClassName.of("TestcontainersConfiguration");

private final ProjectDescription description;

private final ServiceConnections serviceConnections;
Expand Down Expand Up @@ -88,7 +91,20 @@ public void contribute(Path projectRoot) throws IOException {
* Contribute code using the specified {@link SourceCode}.
* @param sourceCode the source code to use for contributions
*/
protected abstract void contributeCode(S sourceCode);
protected void contributeCode(S sourceCode) {
createTestcontainersConfiguration(sourceCode);
}

private void createTestcontainersConfiguration(S sourceCode) {
C testcontainersConfigurationUnit = sourceCode.createCompilationUnit(this.description.getPackageName(),
TESTCONTAINERS_CONFIGURATION_CLASS_NAME.getSimpleName());
T testcontainersConfiguration = testcontainersConfigurationUnit
.createTypeDeclaration(TESTCONTAINERS_CONFIGURATION_CLASS_NAME.getSimpleName());
testcontainersConfiguration.annotations()
.add(TEST_CONFIGURATION_CLASS_NAME, (annotation) -> annotation.set("proxyBeanMethods", false));
this.serviceConnections.values()
.forEach((serviceConnection) -> configureServiceConnection(testcontainersConfiguration, serviceConnection));
}

protected abstract void configureServiceConnection(T typeDeclaration, ServiceConnection serviceConnection);

Expand All @@ -101,10 +117,6 @@ protected void customizeApplicationCompilationUnit(S sourceCode, Consumer<C> cus
protected void customizeApplicationTypeDeclaration(S sourceCode, Consumer<T> customizer) {
customizeApplicationCompilationUnit(sourceCode, (compilationUnit) -> {
T applicationType = compilationUnit.createTypeDeclaration(getTestApplicationName());
applicationType.annotations()
.add(TEST_CONFIGURATION_CLASS_NAME, (annotation) -> annotation.set("proxyBeanMethods", false));
this.serviceConnections.values()
.forEach((serviceConnection) -> configureServiceConnection(applicationType, serviceConnection));
customizer.accept(applicationType);
});
}
Expand All @@ -118,7 +130,7 @@ protected void annotateContainerMethod(Annotatable annotable, String name) {
});
}

protected String getTestApplicationName() {
private String getTestApplicationName() {
return "Test" + this.description.getApplicationName();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -127,6 +127,11 @@ JavaTestContainersApplicationCodeProjectContributor javaTestContainersApplicatio
this.description, serviceConnections);
}

@Bean
ImportTestcontainersConfigurationTestApplicationTypeCustomizer importTestcontainersConfigurationTestApplicationTypeCustomizer() {
return new ImportTestcontainersConfigurationTestApplicationTypeCustomizer();
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -53,7 +53,7 @@ void declaresOracleFreeContainerBeanWithBoot32() {
ProjectRequest request = createProjectRequest("testcontainers", "oracle");
request.setBootVersion(SPRING_BOOT_VERSION);
request.setLanguage("java");
assertThat(generateProject(request)).textFile("src/test/java/com/example/demo/TestDemoApplication.java")
assertThat(generateProject(request)).textFile("src/test/java/com/example/demo/TestcontainersConfiguration.java")
.contains("import org.testcontainers.oracle.OracleContainer;")
.contains(" return new OracleContainer(DockerImageName.parse(\"gvenzl/oracle-free:latest\"));");
}
Expand Down
Loading

0 comments on commit 4cadd87

Please sign in to comment.