Spring Boot Unit Test a module not detecting an autowired component

35,031

Solution 1

Provide your test with @ContextConfiguration(classes = ClassToAutowire.class), then your context will be provided with that class. @SpringBootTest annotation is used in Integration Tests to load full context. With @ContextConfiguration annotation you are able to load only part of context.

Solution 2

@SpringBootTest is used for integration tests that's mean integrating different layers of the application. This is why it loads the whole context.

If you want to unit test only controllers, use @WebMvcTest and mock all the other layers that you need.

For full unit tests of all the layers with working examples see: Testing in Spring

Last date of retrieval: 2018/27/06

Share:
35,031

Related videos on Youtube

Carmageddon
Author by

Carmageddon

Updated on July 09, 2022

Comments

  • Carmageddon
    Carmageddon almost 2 years

    we have split our Maven based Spring Boot project into two modules as follows:

    ProjectRoot
    -SharedModel
    -Application
    --main
    ---java
    ----com....(Application.java)
    -----com....(ClassToAutowire.java)
    --test
    ----com....(ApplicationTest.java)
    -----com....(ClassToAutowireTest.java)
    

    The test class looks as follows:

    @RunWith(SpringRunner.class)
    public class ClassToAutowireTest extends BaseTest {
    
        @Autowired
        private ClassToAutowire classToAutowire;
    
        @Before
        public void setup() throws IOException {        
        ....
        }
    
        @Test
        public void someTest() {
            ....
            assertEquals(this.classToAutowire.isSupported(this.message), true);
        }
    
    }
    

    The ClassToAutowire looks like the follows:

    @Component
    public class ClassToAutowire {
    
        private ConfigurationService configurationService;
    
        @Autowired
        public ClassToAutowire(ConfigurationService configurationService) {
            this.configurationService = configurationService;
        }
    
    
    
        ....
    
    }
    

    The Application.java looks as follows:

    @SpringBootApplication
    @EnableRetry
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    And the ApplicationTest.java looks as follows:

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class ApplicationTests {
    
        @Test
        @Ignore
        public void contextLoads() {
            // °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸
        }
    }
    

    When I run this Unit Test, we get the following trace:

        2018-06-27 15:02:37.835 ERROR [                                main] context.TestContextManager.prepareTestInstance(TestContextManager.java:234): Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@57c03d88] to prepare test instance [com.some.package.structure.ClassToAutowireTest@16aa8654]
    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.some.package.structure.ClassToAutowireTest': Unsatisfied dependency expressed through field 'classToAutowire'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.some.package.structure.ClassToAutowire' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:386)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
        at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
        at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
        at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.some.package.structure.ClassToAutowire' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1493)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
        ... 27 more
    

    Looks like this problem can be bypassed by adding the annotation @SpringBootTest(classes={ClassToAutowire.class}) below the @RuneWith annotation, but then the unit test seems to load the whole SpringBoot app to run test.

    My questions are:

    1) Why is this extra annotation needed to workaround the problem? It is supposed to scan the entire Application/main/java/.... (based on Application.java), so why is it not detecting the component?

    2) How best to do it, with minimal loading to make unit tests faster?