I have an ApplicationRunner
implementation named RefAppRunner
which performs some logic.
I would like to write a SpringBoot based unit test to validate that ApplicationRunner
behaves as expected based on the ApplicationArguments
it gets.
The implementation also has some @Autowired
fields.
What is the recommended way of unit testing such a component in a Spring Boot application?
I want all the Spring based context to be initialized as though the app is launched properly, but I want to supply variations of the ApplicationArguments
Is there a recommended way to write such tests?
I have commented out @SpringBootTest
because if it is enabled, then the custom ApplicationRunner
will have been invoked before the run
test method is invoked .. meaning there is no control over the values passed to the impl.
Here is my current Java unit test in a Spring Boot project:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.DefaultApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;
//@SpringBootTest
public class RefAppRunnerTest
{
@Autowired
private RefAppRunner refAppRunner;
@Test
public void run() throws Exception {
refAppRunner.run(new DefaultApplicationArguments(new String[]{}));
}
}
RefAppRunner
is an ApplicationRunner
implementation:
@Component
public class RefAppRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
// ..
}
}
But I'm getting this error:
java.lang.NullPointerException: Cannot invoke "com.apps.referenceapp.RefAppRunner.run(org.springframework.boot.ApplicationArguments)" because "this.refAppRunner" is null
at com.apps.referenceapp.RefAppRunnerTest.run(RefAppRunnerTest.java:16)
This a good case use case for ApplicationContextRunner
(Spring Docs):
public class AppRunnerTest {
@Test
public void testAppRunner() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner();
contextRunner
.withUserConfiguration(RefAppRunner.class)
.run(context -> {
Assertions.assertTrue(context.containsBean("refAppRunner"));
RefAppRunner myAppRunner = (RefAppRunner) context.getBean("refAppRunner");
myAppRunner.run(new DefaultApplicationArguments());
});
}
}
Please note that this will configure a new ApplicationContext
for the class that only contains your RefAppRunner
. If your RefAppRunner
has a dependency on e.g. a "MyFancyBean" bean, you will need to supply it to the context as well or the autowiring inside RefAppRunner
will fail:
//...
contextRunner
.withUserConfiguration(RefAppRunner.class, MyFancyBean.class)
//...