Java Spring Boot: Exclude CommandLineRunner in JUnit

Featured image

This will is a quick post on a recent testing problem I faced while programming a Java console app with Spring framework.

My console app class uses the Spring interface CommandLineRunner, see Spring API. The reason we use this, is that any beans implementing this interface will be executed automatically by the SpringApplication. This reduces boiler-plate code and uses Inversion-of-Control (IoC) if we want to load a different app.

The structure of my console app looks like the following:

@SpringBootApplication
public class Application {

    @Bean
    public CommandLineRunner getConsoleApp() {return new MyConsoleApp();}
    }

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class, args);
    }
}

class MyConsoleApp implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception { ... }
}

Testing with JUnit

Since we are using IoC and dependency injections, we need to setup the ApplicationContext before executing our tests. This is generally done by adding the annotation @SpringBootTest to our test class.

If we want to test a part of the code, there are times when we don’t want to run the console application. But the problem is that the SpringApplication will execute the CommandLineRunner bean.

While searching online for a solution, I found this post and spring.io that describe @Configuration and @TestConfiguration classes, nested inside the JUnit test class. Extracted from docs.spring.io:

If you want to customize the primary configuration, you can use a nested @TestConfiguration class. Unlike a nested @Configuration class, which would be used instead of your application’s primary configuration, a nested @TestConfiguration class is used in addition to your application’s primary configuration.

In short,

Because the console app is annotated with @SpringBootApplication, I decided to create a nested @Configuration class that extends the original configuration held by Application. Then, I override the bean configuration for CommandLineRunner.

Here’s how my code looks like in my test class (using JUnit 5):

@SpringBootTest
class TimeVolumeProfileImporterTest {

    @Configuration
    static class MyTestConfiguration extends Application {
        @Bean
        @Override
        public CommandLineRunner getConsoleApp() { return null; }
    }

    @Test
    void testMethod() { ... }
}

For this test class, the SpringApplication will not run the CommandLineRunner, but IoC and dependency injections are still applied.

Alternative solutions

Other solutions are: