{"id":18645821,"url":"https://github.com/spring-projects-experimental/spring-boot-testjars","last_synced_at":"2025-07-14T05:36:07.273Z","repository":{"id":216567585,"uuid":"730350522","full_name":"spring-projects-experimental/spring-boot-testjars","owner":"spring-projects-experimental","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-18T18:47:42.000Z","size":310,"stargazers_count":68,"open_issues_count":28,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-18T19:39:50.017Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spring-projects-experimental.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-12-11T18:23:31.000Z","updated_at":"2025-06-18T18:47:45.000Z","dependencies_parsed_at":"2024-06-06T22:12:45.861Z","dependency_job_id":"07a94c20-aa15-46be-a47e-d1443c9008b2","html_url":"https://github.com/spring-projects-experimental/spring-boot-testjars","commit_stats":null,"previous_names":["spring-projects-experimental/spring-boot-testjars"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/spring-projects-experimental/spring-boot-testjars","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects-experimental%2Fspring-boot-testjars","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects-experimental%2Fspring-boot-testjars/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects-experimental%2Fspring-boot-testjars/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects-experimental%2Fspring-boot-testjars/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spring-projects-experimental","download_url":"https://codeload.github.com/spring-projects-experimental/spring-boot-testjars/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spring-projects-experimental%2Fspring-boot-testjars/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265246023,"owners_count":23734109,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T06:17:20.222Z","updated_at":"2025-07-14T05:36:04.345Z","avatar_url":"https://github.com/spring-projects-experimental.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"= spring-boot-testjars\n:TESTJARS_VERSION: 0.0.3\n\nhttps://docs.spring.io/spring-boot/docs/3.2.1/reference/html/features.html#features.testcontainers[Spring Boot + Testcontainers support] for Spring Boot applications, but without the need for a Docker container.\n\nThis 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:\n\n* Adding the ability to easily \u003c\u003cstart-external,start an External Spring Boot application\u003e\u003e\n* \u003c\u003cdynamicproperty,Extending Spring Boot's support for `DynamicPropertyRegistry`\u003e\u003e\n\nNOTE: This project is an experimental project and may make breaking changes including, but not limited to, the name of the project.\n\n== Videos\n\n* https://spring.io/blog/2024/02/08/spring-tips-spring-boot-testjars[Spring Tips]\n\n== Motivation\n\nWhy not just create a Docker image of the Spring Boot application and use Testcontainers?\n\n* Lighter weight than spinning up a Docker image\n* When you are consuming dependencies, you don't always have a docker image available. Sure you can create one, but why add additional overhead?\n\n== Maven / Gradle\n\nYou can add Spring Boot Testjars to your project by adding them to your Gradle or Maven build.\n\n.build.gradle\n[source,groovy,subs=attributes+]\n----\ntestImplementation(\"org.springframework.experimental.boot:spring-boot-testjars:{TESTJARS_VERSION}\")\n----\n\n.pom.xml\n[source,xml,subs=attributes+]\n----\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.experimental.boot\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-testjars\u003c/artifactId\u003e\n    \u003cversion\u003e{TESTJARS_VERSION}\u003c/version\u003e\n\u003c/dependency\u003e\n----\n\nReleases are published to Maven Central.\nFor any other release type, refer to the https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Artifacts#spring-repositories[Spring Repositories].\n\n\n[[starting-external]]\n== Starting an External Spring Boot Application\n\nThis project allows users to easily start an external Spring Boot application by creating it as a Bean.\nFor 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:\n\n[source,java]\n----\n@Bean\nstatic CommonsExecWebServerFactoryBean messagesApiServer() {\n  return CommonsExecWebServerFactoryBean.builder()\n    .classpath((cp) -\u003e cp\n        .files(\"build/libs/messages-0.0.1-SNAPSHOT.jar\")\n    );\n}\n----\n\nThe `CommonsExecWebServerFactoryBean` creates a `CommonsExecWebServer` and the property `CommonsExecWebServer.getPort()` returns the port that the application starts on.\n\n=== MavenClasspathEntry\n\nUser's can also resolve Maven dependencies from Maven Central.\nThe following will add spring-boot-starter-authorization-server and it's transitive dependencies to the classpath.\n\n[source,java]\n----\n@Bean\n@OAuth2ClientProviderIssuerUri\nstatic CommonsExecWebServerFactoryBean authorizationServer() {\n\t// @formatter:off\n\treturn CommonsExecWebServerFactoryBean.builder()\n\t\t// ...\n\t\t.classpath((classpath) -\u003e classpath\n\t\t\t// Add spring-boot-starter-authorization-server \u0026 transitive dependencies\n\t\t\t.entries(springBootStarter(\"oauth2-authorization-server\"))\n\t\t);\n\t// @formatter:on\n}\n----\n\nYou can also use the `MavenClasspathEntry` constructor directly or additional helper methods to add dependencies other than Spring Boot starters to the classpath.\n\nTo use this feature, add the following to your build:\n\n.build.gradle\n[source,groovy,subs=attributes+]\n----\ntestImplementation 'org.springframework.experimental.boot:spring-boot-testjars-maven:{TESTJARS_VERSION}'\n----\n\n.pom.xml\n[source,xml,subs=attributes+]\n----\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.experimental.boot\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-testjars-maven\u003c/artifactId\u003e\n    \u003cversion\u003e{TESTJARS_VERSION}\u003c/version\u003e\n\u003c/dependency\u003e\n----\n\n==== Custom Maven Repositories\n\nBy default, the following repositories are added:\n- Maven Central\n- If it is a SNAPSHOT dependency with a group id that starts with `org.springframework`, then Spring's milestone and snapshot repositories are added\n- If it is a milestone or release candidate dependency with a group id that starts with `org.springframework`, then Spring's milestone repository is added\n\nYou can customize the repositories that are searched by injecting the repositories like the example below:\n\n[source,java]\n----\nList\u003cRemoteRepository\u003e repositories = new ArrayList\u003c\u003e();\nrepositories.add(new RemoteRepository.Builder(\"central\", \"default\", \"https://repo.maven.apache.org/maven2/\").build());\nrepositories.add(new RemoteRepository.Builder(\"sonatype-snapshot\", \"default\", \"https://oss.sonatype.org/content/repositories/snapshots/\").build());\nMavenClasspathEntry classpathEntry = new MavenClasspathEntry(\"org.junit:junit5-api:5.0.0-SNAPSHOT\", repositories);\n----\n\n==== Exclude Transitive Dependencies\n\nIf you are resolving a Spring Boot (fat jar), then you do not need to resolve transitive dependencies.\nTo disable downloading transitive dependencies, you can pass in `true` for the `excludeTransitives` constructor argument.\nFor example:\n\n[source,java]\n----\nboolean excludeTransitives = true;\nList\u003cRemoteRepository\u003e repositories = new ArrayList\u003c\u003e();\nrepositories.add(new RemoteRepository.Builder(\"example\", \"default\", \"https://example.org/maven2/\").build());\nMavenClasspathEntry classpathEntry = new MavenClasspathEntry(\"org.example:message-service:1.0.0\", repositories, excludeTransitives);\n----\n\n=== GenericSpringBootApplicationMain\n\nFor adhoc applications, you may not have a main class and you may not want to provide boilerplate main method yourself.\nIf that is the case, you can leverage `useGenericSpringBootMain`:\n\n[source,java]\n----\n@Bean\n@OAuth2ClientProviderIssuerUri\nstatic CommonsExecWebServerFactoryBean authorizationServer() {\n\t// @formatter:off\n\treturn CommonsExecWebServerFactoryBean.builder()\n\t\t// ...\n\t\t// Add a generic class annotated with SpringBootApplication and a main method to the classpath and use it as the main class\n\t\t.useGenericSpringBootMain();\n\t// @formatter:on\n}\n----\n\n=== Default Resources\n\nIf present, `CommonsExecWebServerFactoryBean` will add the resources `testjars/$beanName/**`, then it is automatically added to the classpath as resources renamed without the path of `testjars/$beanName`.\nFor example, if the bean name is `authorizationServer`, then the resource `testjars/authorizationServer/application.yml` will automatically be added to the classpath as `application.yml`.\nFurthermore, a resource named `testjars/authorizationServer/foo/bar.txt` will automatically be added to the classpath as `foo/bar.txt`.\n\n=== Adding Additional Classes to the ApplicationContext\n\nIt can often be helpful to add additional classes to the `ApplicationContext`.\nSimply adding them to the classpath does not necessarily add the class to the `ApplicationContext`.\nFor example, if someone wants to start a Config Server instance, the `ConfigServerConfiguration` must be imported:\n\n[source,java]\n----\n@Bean\n@DynamicPortUrl(name = \"spring.cloud.config.uri\")\nstatic CommonsExecWebServerFactoryBean configServer() {\n\t// @formatter:off\n\treturn CommonsExecWebServerFactoryBean.builder()\n\t\t.useGenericSpringBootMain()\n\t\t.setAdditionalBeanClassNames(\"org.springframework.cloud.config.server.config.ConfigServerConfiguration\")\n\t\t.classpath((classpath) -\u003e classpath\n\t\t\t.entries(springBootStarter(\"web\"))\n\t\t\t.entries(new MavenClasspathEntry(\"org.springframework.cloud:spring-cloud-config-server:4.2.0\"))\n\t\t);\n\t// @formatter:on\n}\n----\n\n=== Debugging\n\nIf you need to start the application in debug mode, you can do so using the `DebugSettings`.\n\n[source,java]\n----\n@Bean\n@OAuth2ClientProviderIssuerUri\nstatic CommonsExecWebServerFactoryBean authorizationServer() {\n\t// @formatter:off\n\treturn CommonsExecWebServerFactoryBean.builder()\n\t\t// ...\n\t\t.debug((settings) -\u003e settings\n\t\t\t.enabled(true)\n\t\t\t// Optional properties with their explicit defaults shown below\n\t\t\t.suspend(true)\n\t\t\t.port(5005)\n\t\t);\n\t// @formatter:on\n}\n----\n\nWhen starting the remote debugger, it is important to remember that the classpath of the `CommonsExecWebServerFactoryBean` is independent of the project it runs in.\nThis means, the classpath of the debugger will need to match the classpat of the `CommonsExecWebServerFactoryBean` rather than the project it exists in.\n\n=== Server Port\n\nBy default `CommonsExecWebServerFactoryBean` starts the application on a random port by specifying the system property `server.port=0`.\nIf you need to disable this behavior, you can opt out using the `useRandomPort` property as shown below:\n[source,java]\n----\n@Bean\n@OAuth2ClientProviderIssuerUri\nstatic CommonsExecWebServerFactoryBean authorizationServer() {\n\t// @formatter:off\n\treturn CommonsExecWebServerFactoryBean.builder()\n\t\t// ...\n\t\t.useRandomPort(false);\n\t// @formatter:on\n}\n----\n\n[[dynamicproperty]]\n== @DynamicProperty\n\nThis is an extension to Spring Boot's existing https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testcontainers.at-development-time.dynamic-properties[`DynamicPropertyRegistry`].\nIt allows annotating arbitrary Spring Bean definitions and adding a property that references properties on that Bean.\n\n=== @EnableDynamicProperty\n\nIn order to use `@DynamicProperty` annotations, it must be enabled with the `@EnableDynamicProperty` annotation as show below:\n\n[source,java]\n----\n@Configuration\n@EnableDynamicProperty\nclass MyConfiguration {\n\t// ...\n}\n----\n\n=== @DynamicProperty Example\n\nFor example, the following `@DynamicProperty` definition uses https://docs.spring.io/spring-framework/reference/core/expressions.html[SpEL] with the current Bean as the https://docs.spring.io/spring-framework/reference/core/expressions/evaluation.html[root object] for the value annotation to add a property named `messages.url` to the URL and the arbitrary available port of the `CommonsExecWebServer`:\n\n[source,java]\n----\n@Bean\n@DynamicProperty(name = \"messages.url\", value = \"'http://localhost:' + port\")\nstatic CommonsExecWebServerFactoryBean messagesApiServer() {\n  return CommonsExecWebServerFactoryBean.builder()\n    .classpath(cp -\u003e cp\n        .files(\"build/libs/messages-0.0.1-SNAPSHOT.jar\")\n    );\n}\n----\n\nNOTE: While our `@DynamicProperty` examples use `CommonsExecWebServer`, the `@DynamicProperty` annotation works with any type of Bean.\n\n=== Composed `@DynamicProperty` Annotations\n\n`@DynamicProperty` is treated as a meta-annotation, so you can create composed annotations with it.\nFor example, the following works the same as our example above:\n\n.MessageUrl.java\n[source,java]\n----\n@Retention(RetentionPolicy.RUNTIME)\n@DynamicProperty(name = \"message.url\", value = \"'http://localhost:' + port\")\npublic @interface MessageUrl {\n}\n----\n\n.Config.java\n[source,java]\n----\n@Bean\n@MessageUrl\nstatic CommonsExecWebServerFactoryBean oauthServer() {\n  return CommonsExecWebServerFactoryBean.builder()\n    .classpath(cp -\u003e cp\n      .files(\"build/libs/authorization-server-0.0.1-SNAPSHOT.jar\")\n    );\n}\n----\n\n=== Well Known Composed `@DynamicProperty` Annotations\n\nThis is a list of well known composed `@DynamicProperty` annotations.\n\n==== @DynamicPortUrl\n\nThis provides a simple way of mapping a property to a URL with a dynamic port that is expressed as the port property on the Bean that is created.\nThe value is calculated as `http://{host}:{port}{contextRoot}`.\n\n* name - the property name to use\n* host - the host to use (default is `localhost`)\n* port - a valid SpEL expression that determines the port to use for the URL (default port)\n* contextRoot - the context root to use (default is empty String)\n\n==== @CloudConfigUri\n\nThis simplifies mapping the to the property `spring.cloud.config.uri`.\nThe value is calculated as `http://{host}:{port}{contextRoot}` such that:\n\n* host - the host to use (default is `localhost`)\n* port - a valid SpEL expression that determines the port to use for the URL (default port)\n* contextRoot - the context root to use (default is empty String)\n\n\n==== @OAuth2ClientProviderIssuerUri\n\nThis provides a mapping to issuer-uri of https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.security.spring.security.oauth2.client.provider[the OAuth provider details].\n\n* name `spring.security.oauth2.client.provider.{providerName}.issuer-uri` with a default `providerName` of `spring`. The `providerName` can be overridden with the `OAuth2ClientProviderIssuerUri.providerName` property.\n* value `'http://127.0.0.1:' + port` which can be overriden with the `OAuth2ClientProviderIssuerUri.value` property\n\n== Samples\nRun xref:samples/oauth2-login/src/test/java/example/oauth2/login/TestOauth2LoginMain.java[TestOauth2LoginMain].\nThis starts the oauth2-login sample and a Spring Authorization Server you assembled in the previous step.\n\nVisit http://localhost:8080/\n\nYou will be redirected to the authorization server.\nLog in using the username `user` and password `password`.\n\nYou are then redirected to the oauth2-login application.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspring-projects-experimental%2Fspring-boot-testjars","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspring-projects-experimental%2Fspring-boot-testjars","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspring-projects-experimental%2Fspring-boot-testjars/lists"}