I have an integration test class annotated with #SpringBootTest which starts up the full application context and lets me execute my tests. However I am unable to #Autowired beans into the test class itself. Instead I get an error:
No qualifying bean of type 'my.package.MyHelper' available".
If I do not #Autowire my helper class, but keep the code directly inside the setUp function, the test works as expected.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
public class CacheControlTest {
#Autowired
private MyHelper myHelper;
#Before
public void setUp() {
myHelper.doSomeStuff();
}
#Test
public void test1() {
// My test
}
}
How can I make use of Spring autowiring inside the test class while also using #SpringBootTest?
Following #user7294900 advice below, creating a separate #Configuration file and adding this at the top of CacheControlTest works:
#ContextConfiguration(classes = { CacheControlTestConfiguration.class })
However is there any way of keeping the configuration inside the CacheControlTest class itself? I have tried adding inside my test class:
public class CacheControlTest {
#TestConfiguration
static class CacheControlTestConfiguration {
#Bean
public MyHelper myHelper() {
return new MyHelper();
}
}
}
And
public class CacheControlTest {
#Configuration
static class CacheControlTestConfiguration {
#Bean
public MyHelper myHelper() {
return new MyHelper();
}
}
}
But they do not seem to have any effect. I still get the same error. The same configuration block works when placed in an separate file as mentioned above though.
Add ContextConfiguration for your Test Class:
#ContextConfiguration(classes = { CacheControlTestConfiguration.class })
Related
In our project, every Junit class(which is annotated using SpringJunitConfig) is having a #Configuration annotated class, which creates the Bean which is required to test any particular Test-class method.
Example:
#SpringJunitConfig
class TestClass {
#Configuration
class TestConfig {
#Bean
public TestClass testClass(DependantBean dependantBean) {
return new TestClass(dependantBean);
}
#Bean
public DependantBean dependantBean() {
return new DependantBean();
}
}
#Autowire private TestClass testClass;
#Test
void testMethod() {
//do testing
}
}
However this looks handy for a single test class, but the issue is every test class is having its own configuration class, which we are trying to avoid and I wanted to have one single configuration class for my whole test classes. Can someone help me to remove this repeated #Configuration?
Thanks in advance.
You could easily create a Configuration meant only for Test cases, and it could be used in #SpringJunitConfig:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/junit/jupiter/SpringJUnitConfig.html
#SpringJUnitConfig is a composed annotation that combines
#ExtendWith(SpringExtension.class) from JUnit Jupiter with
#ContextConfiguration from the Spring TestContext Framework.
Define your TestConfig:
#Configuration
public class ValidationTestSpringConfig {
#Bean
public TaskScheduler validationTaskScheduler() {
ThreadPoolTaskScheduler tpts = new ThreadPoolTaskScheduler();
tpts.setPoolSize(2);
return tpts;
}
}
Then using this #SpringJunitConfig annotation, you can actually provide the context configuration you need, which loads up the Test Beans:
#SpringJUnitConfig(ValidationTestSpringConfig.class)
public class HttpValidationIntegrationTest {
#Autowired
private TaskScheduler taskScheduler;
}
In a Spring Boot application I am working on, I have a class which is not annotated as bean (#Component), but contains an autowired field:
public class One{
#Autowired
private Two x;
public getX(){
return x;
}
}
In the configuration xml of the Spring application the class One is marked as bean which makes that the variable x gets initialized when I run the application.
Now I have written a test that doesn't seem to use the spring xml configuration. So I tried to do it manually:
#RunWith(SpringRunner.class)
public class Test{
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX()); //null
}
}
How can I make Spring inject the correct code so that x is not null in my test?
Just tell the test what config to use:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContext_test.xml" })
public class Test{
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX()); //null
}
}
See here for doc
Alernative to #Essex Boy approach:
use a custom configuration in your test:
#RunWith(SpringRunner.class)
public class TestClass {
#Configuration
#ComponentScan
static class ConfigurationClass {
#Bean
public One makeOne() {
return new One();
}
}
#Autowired
One y;
#Test
public void checkOne(){
System.out.println(y.getX());
}
}
Essex Boy's approach runs an "integration test" because it starts up Spring for the test.
Usually for Unit Tests, you want to mock your depencies; those can be "autowired".
#RunWith(MockitoJUnitRunner.class) // necessary for the annotations to work
public class YourTest {
// this is a mock
#Mock
private Two mockedTwo;
#InjectMocks
// this is automatically created and injected with dependencies
private One sut;
#Test
public void test() {
assertNotNull(sut.getX());
sut.doStuff();
verify(mockedTwo).wasCalled();
}
}
#ContextConfiguration(classes = ConfigureCustomConfigurationModelProviderTest.class)
public class ConfigureCustomConfigurationModelProviderTest extends AbstractContextTest {
#Bean(name = "smth")
public static ConfigurationModelProvider get() {
return AnnotationConfigurationModelProvider.getInstance();
}
/*...*/
}
I'm getting this error since migrating from junit4 to junit5. Why?
You should move every beans to #Configuration class for example TestConfig:
#Configuration
public class TestConfig {
#Bean(name = "smth")
public static ConfigurationModelProvider get() {
return AnnotationConfigurationModelProvider.getInstance();
}
}
and import it via #Import:
#Import({TestConfig.class})
#ContextConfiguration(classes = ConfigureCustomConfigurationModelProviderTest.class)
public class ConfigureCustomConfigurationModelProviderTest extends AbstractContextTest {
}
To add to Amir's answer: if you only need the bean for some tests you can omit the #Configuration annotation at the top.
I recently added an ApplicationStartup class to my SpringBoot project
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> { ...
It implements ApplicationListener.
Now when I run my old JUNit tests that have nothing to do with that class, The testrunner tries to Run my StartupListener, which is neither necessary not appropriate in these cases.
How do I skip the ApplicationListener when my tests initialize?
#RunWith(SpringRunner.class)
#SpringBootTest
public class SubmissionItemManagerTest {...
You can mock your ApplicationStartup class
Add this declaration to your test case:
#MockBean
private ApplicationStartup applicationStartup
This will create a mocked instance of ApplicationStartup and mark it as #Primary in your test context thereby replacing the actual instance ofApplicationStartup.
You can create a separate application class for testing and exclude the components that are not required for tests:
#SpringBootApplication
#ComponentScan(excludeFilters = #ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = { ApplicationStartup.class,
RealApplication.class }))
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Then in your SubmissionItemManagerTest class use the TestApplication class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
public class SubmissionItemManagerTest {
...
}
I have a Spring Boot 1.4.2 application. Some code which is used during startup looks like this:
#Component
class SystemTypeDetector{
public enum SystemType{ TYPE_A, TYPE_B, TYPE_C }
public SystemType getSystemType(){ return ... }
}
#Component
public class SomeOtherComponent{
#Autowired
private SystemTypeDetector systemTypeDetector;
#PostConstruct
public void startup(){
switch(systemTypeDetector.getSystemType()){ // <-- NPE here in test
case TYPE_A: ...
case TYPE_B: ...
case TYPE_C: ...
}
}
}
There is a component which determines the system type. This component is used during startup from other components. In production everything works fine.
Now I want to add some integration tests using Spring 1.4's #MockBean.
The test looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyWebApplication.class, webEnvironment = RANDOM_PORT)
public class IntegrationTestNrOne {
#MockBean
private SystemTypeDetector systemTypeDetectorMock;
#Before
public void initMock(){
Mockito.when(systemTypeDetectorMock.getSystemType()).thenReturn(TYPE_C);
}
#Test
public void testNrOne(){
// ...
}
}
Basically the mocking works fine. My systemTypeDetectorMock is used and if I call getSystemType -> TYPE_C is returned.
The problem is that the application doesn't start. Currently springs working order seems to be:
create all Mocks (without configuration all methods return null)
start application
call #Before-methods (where the mocks would be configured)
start test
My problem is that the application starts with an uninitialized mock. So the call to getSystemType() returns null.
My question is: How can I configure the mocks before application startup?
Edit: If somebody has the same problem, one workaround is to use #MockBean(answer = CALLS_REAL_METHODS). This calls the real component and in my case the system starts up. After startup I can change the mock behavior.
In this case you need to configure mocks in a way we used to do it before #MockBean was introduced - by specifying manually a #Primary bean that will replace the original one in the context.
#SpringBootTest
class DemoApplicationTests {
#TestConfiguration
public static class TestConfig {
#Bean
#Primary
public SystemTypeDetector mockSystemTypeDetector() {
SystemTypeDetector std = mock(SystemTypeDetector.class);
when(std.getSystemType()).thenReturn(TYPE_C);
return std;
}
}
#Autowired
private SystemTypeDetector systemTypeDetector;
#Test
void contextLoads() {
assertThat(systemTypeDetector.getSystemType()).isEqualTo(TYPE_C);
}
}
Since #TestConfiguration class is a static inner class it will be picked automatically only by this test. Complete mock behaviour that you would put into #Before has to be moved to method that initialises a bean.
I was able to fix it like this
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyWebApplication.class, webEnvironment = RANDOM_PORT)
public class IntegrationTestNrOne {
// this inner class must be static!
#TestConfiguration
public static class EarlyConfiguration {
#MockBean
private SystemTypeDetector systemTypeDetectorMock;
#PostConstruct
public void initMock(){
Mockito.when(systemTypeDetectorMock.getSystemType()).thenReturn(TYPE_C);
}
}
// here we can inject the bean created by EarlyConfiguration
#Autowired
private SystemTypeDetector systemTypeDetectorMock;
#Autowired
private SomeOtherComponent someOtherComponent;
#Test
public void testNrOne(){
someOtherComponent.doStuff();
}
}
You can use the following trick:
#Configuration
public class Config {
#Bean
public BeanA beanA() {
return new BeanA();
}
#Bean
public BeanB beanB() {
return new BeanB(beanA());
}
}
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {TestConfig.class, Config.class})
public class ConfigTest {
#Configuration
static class TestConfig {
#MockBean
BeanA beanA;
#PostConstruct
void setUp() {
when(beanA.someMethod()).thenReturn(...);
}
}
}
At least it's working for spring-boot-2.1.9.RELEASE
Spring's initialization is triggered before #Before Mockito's annotation so the mock is not initialized at the time the #PostConstruct annotated method is executed.
Try to 'delay' your system detection using #Lazy annotation on the SystemTypeDetector component. Use your SystemTypeDetector where you need it, keep in mind that you cannot trigger this detection in a #PostConstruct or equivalent hook.
I think that it's due to the way you autowire your dependencies. Take a look at this (specially the part about 'Fix #1: Solve your design and make your dependencies visible'). That way you can also avoid using the #PostConstruct and just use the constructor instead.
What U are using, is good for a unit tests:
org.mockito.Mockito#when()
Try to use the following methods for mocking spring beans when the context is spined-up:
org.mockito.BDDMockito#given()
If u are using #SpyBean, then u should use another syntax:
willReturn(Arrays.asList(val1, val2))
.given(service).getEntities(any());