1

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?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.