1
votes

I am new to Spring MVC and JUnit. Basically I want to autowire the service class and this class should be loaded in spring context.

Service


    @Service 
    public class FundService  {
        @Autowired
        FundDAO fundDAO;

        /**
         * @return
         */
        public List getFundDetails(String productId) {

            return fundDAO.getFundDetails(productId);
        }   
    }

application-context

<beans>

    <mvc:annotation-driven />

    <context:component-scan base-package="com.test.*" />
</beans>

Junit class


    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations={"classpath*:/WEB-INF/application-context.xml"})
    public class CompensationServiceTest {

        @Autowired
        private FundService fundService;

        @Test
        public void verifyGetCompensationList()
        {
            System.out.println(fundService == null);
        }
    }

While executing the test I am getting following exception trace


    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.test.admin.service.CompensationServiceTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.test.admin.service.FundService com.test.admin.service.CompensationServiceTest.fundService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.test.admin.service.FundService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1064)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
        at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:333)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:220)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:301)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:303)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
        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:236)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.test.admin.service.FundService com.test.admin.service.CompensationServiceTest.fundService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.test.admin.service.FundService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:507)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:283)
        ... 26 more
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.test.admin.service.FundService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:903)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:772)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:686)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
        ... 28 more

5

5 Answers

2
votes

First of all, the WEB-INF folder should never be in the classpath; it should be in the file system of the project. So if you use the Maven project structure, that folder would be located relative to the root of your project (i.e., src/main/webapp/WEB-INF). In that case you would want to declare an XML configuration file in that folder as a file system resource like this:

@ContextConfiguration("file:src/main/webapp/WEB-INF/application-context.xml")

Secondly, if this is a test configuration file, you should not store it under WEB-INF. Instead, you should store it in the classpath. Following the Maven project structure, this would be src/test/resources/application-context.xml, in which case you would use the following declaration in your test:

@ContextConfiguration("/application-context.xml")

or

@ContextConfiguration("classpath:application-context.xml")

... whichever your prefer, but note that they are equivalent.

Regards and thanks,

Sam

2
votes

There are two options:

If you use @Autowire it means you're creating an application context for your tests, there are several steps to achieve this; Instead of telling you how, I'd like to tell you about the right approach.

Since you're talking about unit tests it means the class you're testing is isolated and the injected dependencies have to be mocks, an example next:

The class:

@RestController
@RequestMapping(value = "/")
public class CatalogRest {

    @Autowired
    private CatalogService catalogService;

    @Autowired
    private LoggedUser loggedUser;

    @RequestMapping(value = GET_VIEW_TYPE, method = POST)
    public ResponseEntity<List<ViewType>> getViewType() {

        List<ViewType> viewTypes = catalogService.getViewType(loggedUser.getUserId());
        return new ResponseEntity<List<ViewType>>(viewTypes, HttpStatus.OK);

    }

Now the test:

 @RunWith(MockitoJUnitRunner.class)
public class CatalogRestTest {

    @InjectMocks
    private CatalogRest subject;

    @Mock
    private CatalogService catalogService;

    @Mock
    private LoggedUser loggedUser;

    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void classAndItsMethodsHaveRequiredAnnotations() throws Exception {
        assertTrue(subject.getClass().isAnnotationPresent(RestController.class));
        assertTrue(subject.getClass().isAnnotationPresent(RequestMapping.class));

        Method getViewTypeMethod = subject.getClass().getMethod("getViewType");
        getViewTypeMethod.isAnnotationPresent(RequestMapping.class);
    }

    @Test
    public void getViewType_whenInvoked_returnsAListOfViewTypes() throws Exception {
        List<ViewType> viewTypes = Arrays.asList(new ViewType());

        when(loggedUser.getUserId()).thenReturn("fake user");
        when(catalogService.getViewType(eq("fake user"))).thenReturn(viewTypes);

        ResponseEntity<List<ViewType>> result = subject.getViewType();

        assertNotNull(result);
        assertNotNull(result.getBody());
        assertTrue(result.getBody().size() == 1);

        verify(catalogService, times(1)).getViewType(eq("fake user"));
    }

Do not attempt to use spring for unit testing, it only should be used for integration testing.

0
votes

You have to qualify the Service class with "@Component" annotation for it to be picked during component scan, right?

0
votes

Can you post the code where you do your bean configuration. The problem here is that Spring is trying to find a concrete class to wire for

@Autowired
private FundService fundService;

You need to specify the bean Spring should inject in your configuration. For example, doing it in XML you would say:

<beans>

    <mvc:annotation-driven />
    <context:component-scan base-package="com.test.*" />

    <!-- Assuming FundServiceImpl is a class that implement FundService -->
    <bean id="fundService" class="com.test.admin.service.FundServiceImpl"/> 
</beans>
0
votes

In my case I had nested classed / param'ed tests:

BEFORE

@RunWith(Enclosed.class)
@ContextConfiguration(classes = {TestX.class})
public class SimpleTest {

...
@RunWith(Parameterized.class)
public static class SimpleCheckAddTest{
.....
}

@RunWith(Parameterized.class)
public static class SimpleCheckRemoveTest{
.....
}

}

and got: <.....Unsatisfied dependency expressed through field......> exception like.

AFTER

@RunWith(Enclosed.class)
public class SimpleTest {

...
@RunWith(Parameterized.class)
@ContextConfiguration(classes = {TestX.class})
public static class SimpleCheckAddTest{
.....
}

@RunWith(Parameterized.class)
@ContextConfiguration(classes = {TestX.class})
public static class SimpleCheckRemoveTest{
.....
}

}