Prevent Application / CommandLineRunner classes from executing during JUnit testing
SpringSpring BootSpring Problem Overview
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:
- to remove the
CommandLineRunner
interface from myApplication
class - to have a different context for testing
Both this solution requires lot of coding. Do you have a more convenient solution?
Spring Solutions
Solution 1 - Spring
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.
Solution 2 - Spring
As mentioned in spring documentation http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html you can use @ContextConfiguration with a special initializer:
> ConfigFileApplicationContextInitializer is an ApplicationContextInitializer that can apply to your tests to load Spring Boot application.properties files. You can use this when you don’t need the full features provided by @SpringApplicationConfiguration.
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 = ConfigFileApplicationContextInitializer.class)
public class ExtractorTest {
@Autowired
AnyComponent anyComponent;
@Test
public void testAnyComponent() {
anyComponent.anyMethod(anyArgument);
}
}
Solution 3 - Spring
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.
Solution 4 - Spring
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"})
Solution 5 - Spring
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;
Solution 6 - Spring
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 {}
Solution 7 - Spring
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.