74

If in your TestCase class there is this annotations:

@SpringApplicationConfiguration(classes = {Application.class})

this will cause the Application.class, implementing the CommandLineRunner interface, to run the required method

public void run(String... args) throws Exception

I still think this is, mostly, a not wanted behaviour, since in your test environment you may not want to launch the entire application.

I have in mind two solution to circumvent this problem:

  1. to remove the CommandLineRunner interface from my Application class
  2. to have a different context for testing

Both this solution requires lot of coding. Do you have a more convenient solution?

3
  • 2
    Can you give an example what you want to test? If you want to test something without the whole application than you wouldn't need the annotation.
    – alexvetter
    Commented Mar 30, 2015 at 11:17
  • 3
    I would like to test the whole application (let's say with all the registered beans). What I dislike is that the whole application starts, due to the CommandLineRunner interface implementation. There should be a way to load such an application class without starting the run method.
    – Gorgia666
    Commented Apr 1, 2015 at 9:23
  • 1
    This is exactly my opinion, it is an unwanted side effect or at least SpringRunner should give you the option to prevent it. and start the context when everything in test is setup.
    – mohamnag
    Commented Feb 28, 2018 at 8:35

7 Answers 7

59

Jan's solution can be achieved easier.

In your test class, activate the "test" profile:

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
public class MyFancyTest {}

In your CommandLineRunner set the profile to NOT test:

@Component
@Profile("!test")
public class JobCommandLineRunner implements CommandLineRunner {}

Then you don't have to manually set the profile in the Application.

5
  • Does it mean that for an every test file I should place this an additional @ActiveProfiles("test") annotation?
    – dnim
    Commented Aug 15, 2018 at 6:05
  • Yes @dnim. For every set of tests you want to run.
    – Michael P.
    Commented Aug 16, 2018 at 10:11
  • 4
    It is no good idea to change production code only to make it testable. Even the profile name "!test" is the very impersonation of a testified production-code. Commented Sep 18, 2019 at 10:30
  • i prefer Jan Petzold's way with setAdditionalProfiles() because @ActiveProfiles doesnt let you add other profiles. This i often need for running the tests in different environments, ie. on dev machine and within CI server.
    – elonderin
    Commented Jul 21, 2020 at 9:10
  • I agree to "It is no good idea to change production code only to make it testable", but even when I try, it doesn't work for me. Applications run method still runs.
    – Daniel D.
    Commented Dec 20, 2023 at 18:20
29

As mentioned in the spring documentation you can use @ContextConfiguration with a special initializer:

ConfigDataApplicationContextInitializer is an ApplicationContextInitializer that you can apply to your tests to load Spring Boot application.properties files. You can use it when you do not need the full set of features provided by @SpringBootTest

In this example anyComponent is initialized and properties are injected, but run(args) methods won't be executed. (Application.class is my main spring entry point)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, 
                      initializers = ConfigDataApplicationContextInitializer.class)
public class ExtractorTest {
    @Autowired
    AnyComponent anyComponent;
    
    @Test
    public void testAnyComponent() {
       anyComponent.anyMethod(anyArgument);
    }
}
1
  • 1
    Ok for CommandLineRunner, but what about ApplicationRunner? I was unable to make a SpringBootApplication (implementing ApplicationRunner) run only once: the first automatically without arguments and the second one manually with my required arguments.
    – linuxatico
    Commented Feb 27, 2019 at 14:15
27

You can define a test configuration in the same package as your application that looks exactly the same, except that it excludes beans implementing CommandLineRunner. The key here is @ComponentScan.excludeFilters:

@Configuration
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CommandLineRunner.class))
@EnableAutoConfiguration
public class TestApplicationConfiguration {
}

Then, just replace the configuration on your test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplicationConfiguration.class)
public class SomeApplicationTest {
    ...
}

No CommandLineRunner will be executed now, because they are not part of the configuration.

5
  • 4
    This answer is very clever, it just misses one bit : you must also exclude the Application.class in the excludeFilters attribute, or it will scan the beans you tried to exclude. Commented Nov 10, 2015 at 15:24
  • 3
    SpringApplicationConfiguration @deprecated as of 1.4 in favor of SpringBootTest. Can be replaced by @SprinBootTest(classes = TestApplicationConfiguration.class)
    – Gangaraju
    Commented Nov 29, 2016 at 5:39
  • 3
    Excluding Application.class will also exclude all the beans you may / may not need. Commented Apr 11, 2018 at 23:52
  • 1
    Please give an example of excluding the Application.class? Commented Jun 15, 2019 at 1:53
  • I guess we can omit the annotation @RunWith(SpringJUnit4ClassRunner.class) Commented Nov 30, 2020 at 16:41
22

I'm a bit late to the party, but a reasonable approach is to mark the bean with @ConditionalOnProperty, e.g.

@ConditionalOnProperty(prefix = "job.autorun", name = "enabled", havingValue = "true", matchIfMissing = true)
public CommandLineRunner myRunner() {...}

The following annotation will then disable it in tests:

@SpringBootTest(properties = {"job.autorun.enabled=false"})
1
  • 1
    This is the best solution when you still need to use the annotations on your Main class to configure the application before the test but need to avoid a specific bean from running. Perfect for a command line application that has a bean that "boots up" everything. Thanks. Commented Jul 26, 2017 at 12:59
6

If you have a mocking framework installed (e.g. MockMVC) you can create a mock instance of the CommandLineRunner implementation, more or less disabling it:

@MockBean private TextProcessor myProcessor;

1
  • 1
    This answer is also suitable for @EventListener(ApplicationReadyEvent.class) approach Commented Aug 8, 2019 at 7:24
2

Previous answers didn't work wor me. I ended up using different profiles - example for the init method in Spring Boot:

SpringApplication app = new SpringApplication(AppConfig.class);
app.setAdditionalProfiles("production");
app.run(args);

This is not executed during the tests so we're safe here.

All tests have their own profile "test" (which is useful in many other ways, too):

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
public class MyFancyTest {}

The command-line runner is annotated with the "production" profile so the tests ignore it:

@Component
@Profile("production")
public class JobCommandLineRunner implements CommandLineRunner {}
1

I solve this by not implementing CommandLineRunner. Just get a bean from the context, and call a method on it, passing argv. That way you will get the same result, and the application won't start automatically when running the tests.

Not the answer you're looking for? Browse other questions tagged or ask your own question.