Spring Boot + Testcontainers support for Spring Boot applications, but without the need for a Docker container.
This project is intended to add support for running external Spring Boot applications during development and testing by building on Spring Boot’s existing support. It is composed of two main features:
-
Adding the ability to easily start an External Spring Boot application
Note
|
This project is an experimental project and may make breaking changes including, but not limited to, the name of the project. |
Why not just create a Docker image of the Spring Boot application and use Testcontainers?
-
Lighter weight than spinning up a Docker image
-
When you are consuming dependencies, you don’t always have a docker image available. Sure you can create one, but why add additional overhead?
You can add Spring Boot Testjars to your project by adding them to your Gradle or Maven build.
testImplementation("org.springframework.experimental.boot:spring-boot-testjars:$TESTJARS_VERSION")
<dependency>
<groupId>org.springframework.experimental.boot</groupId>
<artifactId>spring-boot-testjars</artifactId>
<version>0.0.1</version>
</dependency>
Releases are published to Maven Central. For any other release type, refer to the Spring Repositories.
This project allows users to easily start an external Spring Boot application by creating it as a Bean. For example, the code below will start (on a arbitrary available port) and stop an external Spring Boot application as a part of the lifecycle of the Spring container:
@Bean
static CommonsExecWebServerFactoryBean messagesApiServer() {
return CommonsExecWebServerFactoryBean.builder()
.classpath((cp) -> cp
.files("build/libs/messages-0.0.1-SNAPSHOT.jar")
);
}
The CommonsExecWebServerFactoryBean
creates a CommonsExecWebServer
and the property CommonsExecWebServer.getPort()
returns the port that the application starts on.
User’s can also resolve Maven dependencies from Maven Central. The following will add spring-boot-starter-authorization-server and it’s transitive dependencies to the classpath.
@Bean
@OAuth2ClientProviderIssuerUri
static CommonsExecWebServerFactoryBean authorizationServer() {
// @formatter:off
return CommonsExecWebServerFactoryBean.builder()
// ...
.classpath((classpath) -> classpath
// Add spring-boot-starter-authorization-server & transitive dependencies
.entries(springBootStarter("oauth2-authorization-server"))
);
// @formatter:on
}
You can also use the MavenClasspathEntry
constructor directly or additional helper methods to add dependencies other than Spring Boot starters to the classpath.
To use this feature, use the feature variant named maven
by adding it to your Gradle or Maven build.
testImplementation ('org.springframework.experimental.boot:spring-boot-testjars:0.0.1') {
capabilities {
requireCapability("org.springframework.experimental.boot:spring-boot-testjars-maven")
}
}
<dependency>
<groupId>org.springframework.experimental.boot</groupId>
<artifactId>spring-boot-testjars</artifactId>
<classifier>maven</classifier>
<version>0.0.1</version>
</dependency>
By default, only Maven Central is searched. You can customize the repositories that are searched by injecting the repositories like the example below:
List<RemoteRepository> repositories = new ArrayList<>();
repositories.add(new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2/").build());
repositories.add(new RemoteRepository.Builder("spring-milestone", "default", "https://repo.spring.io/milestone/").build());
MavenClasspathEntry classpathEntry = new MavenClasspathEntry("org.springframework:spring-core:6.1.0-RC1", repositories);
In some cases you need to provide a Java main class without any additional configuration. If that is the case, you can do it with the following:
@Bean
@OAuth2ClientProviderIssuerUri
static CommonsExecWebServerFactoryBean authorizationServer() {
// @formatter:off
return CommonsExecWebServerFactoryBean.builder()
// ...
// Add a class annotated with SpringBootApplication and a main method to the classpath and use it as the main class
.defaultSpringBootApplicationMain();
// @formatter:on
}
If present, CommonsExecWebServerFactoryBean
will add the resources webjars/$beanName/application.yml
or webjars/$beanName/application.properties
exists, then it is automatically added to the classpath as application.yml
and application.properties
respectively.
This is an extension to Spring Boot’s existing DynamicPropertyRegistry
.
It allows annotating arbitrary Spring Bean definitions and adding a property that references properties on that Bean.
For example, the following @DynamicProperty
definition uses SpEL with the current Bean as the root object for the value annotation to add a property named messages.url
to the URL and the arbitrary available port of the CommonsExecWebServer
:
@Bean
@DynamicProperty(name = "messages.url", value = "'http://localhost:' + port")
static CommonsExecWebServerFactoryBean messagesApiServer() {
return CommonsExecWebServerFactoryBean.builder()
.classpath(cp -> cp
.files("build/libs/messages-0.0.1-SNAPSHOT.jar")
);
}
Note
|
While our @DynamicProperty examples use CommonsExecWebServer , the @DynamicProperty annotation works with any type of Bean.
|
@DynamicProperty
is treated as a meta-annotation, so you can create composed annotations with it.
For example, the following works the same as our example above:
@Retention(RetentionPolicy.RUNTIME)
@DynamicProperty(name = "message.url", value = "'http://localhost:' + port")
public @interface MessageUrl {
}
@Bean
@MessageUrl
static CommonsExecWebServerFactoryBean oauthServer() {
return CommonsExecWebServerFactoryBean.builder()
.classpath(cp -> cp
.files("build/libs/authorization-server-0.0.1-SNAPSHOT.jar")
);
}
This is a list of well known composed @DynamicProperty
annotations.
This provides a mapping to issuer-uri of the OAuth provider details.
-
name
spring.security.oauth2.client.provider.${providerName}.issuer-uri
with a defaultproviderName
ofspring
. TheproviderName
can be overridden with theOAuth2ClientProviderIssuerUri.providerName
property. -
value
'http://127.0.0.1:' + port
which can be overriden with theOAuth2ClientProviderIssuerUri.value
property
Run TestOauth2LoginMain. This starts the oauth2-login sample and a Spring Authorization Server you assembled in the previous step.
Visit http://localhost:8080/
You will be redirected to the authorization server.
Log in using the username user
and password password
.
You are then redirected to the oauth2-login application.