I am trying to create a fat JAR that includes my application and its tests to run them externally using the JUnit Console Launcher. The build succeeds, and the JAR is created. However, when I execute the tests, the ones that rely on Spring's dependency injection (like @Autowired TestRestTemplate) fail because the TestRestTemplate bean is null.
My application and a simple unit test work fine, but the integration test that uses the autowired TestRestTemplate fails.
build.gradle.kts
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-test")
implementation(platform("org.junit:junit-bom:5.10.0"))
implementation("org.junit.jupiter:junit-jupiter")
}
tasks.named<ShadowJar>("shadowJar") {
archiveClassifier.set("fat-tests-shadow")
from(sourceSets.test.get().output)
configurations = listOf(project.configurations.runtimeClasspath.get())
manifest {
attributes(
"Main-Class" to "org.springframework.boot.loader.launch.JarLauncher",
"Start-Class" to "org.example.demo.DemoApplication"
)
}
}
DemoApplication.java
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@RestController
class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, Spring Boot!";
}
}
DemoApplicationTests.java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoApplicationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void contextLoads() {
// Basic test to verify application context loads successfully
}
@Test
void helloEndpointShouldReturnDefaultMessage() {
String url = "http://localhost:" + port + "/hello";
String response = this.restTemplate.getForObject(url, String.class);
assertThat(response).isEqualTo("Hello, Spring Boot!");
}
@Test
void helloControllerShouldBeAvailable() {
HelloController controller = new HelloController();
assertThat(controller.hello()).isEqualTo("Hello, Spring Boot!");
}
}
Command to run tests
java -jar junit-platform-console-standalone-1.9.3.jar \
--classpath build/libs/someStuff-1.0-SNAPSHOT-fat-tests-shadow.jar \
--include-classname=".*" \
--scan-classpath \
--exclude-engine=junit-vintage
Console output
.
+-- JUnit Jupiter [OK]
| +-- DemoApplicationTest [OK]
| | +-- helloControllerShouldBeAvailable() [OK]
| | +-- contextLoads() [OK]
| | '-- helloEndpointShouldReturnDefaultMessage() [X] Cannot invoke "org.springframework.boot.test.web.client.TestRestTemplate.getForObject(String, java.lang.Class, Object[])" because "this.restTemplate" is null
| '-- ArithmeticTest [OK]
| '-- twoPlusTwo() [OK]
'-- JUnit Platform Suite [OK]
Failures (1):
JUnit Jupiter:DemoApplicationTest:helloEndpointShouldReturnDefaultMessage()
MethodSource [className = 'org.example.demo.DemoApplicationTest', methodName = 'helloEndpointShouldReturnDefaultMessage', methodParameterTypes = '']
=> java.lang.NullPointerException: Cannot invoke "org.springframework.boot.test.web.client.TestRestTemplate.getForObject(String, java.lang.Class, Object[])" because "this.restTemplate" is null
The test helloEndpointShouldReturnDefaultMessage() fails with a NullPointerException because the autowired restTemplate is null. The other tests, which do not rely on Spring's context, pass. This suggests that when the tests are run from the fat JAR, the Spring application context is not being started properly, or the beans are not being injected.
The test runs successfully inside my IDE and when using ./gradlew test
My question is: How can I configure the fat JAR or the test execution so that the Spring Boot application context starts correctly and dependency injection works when running tests via the JUnit Console Launcher?