Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service not been injected in Spring controller test with spring-test-mvc

I'm attempting to build a set of unit/integration tests for a Restful API Server I have created using Spring MVC 3.1.1.

I'm trying to use the Spring-test-mvc.

I'm relatively new to Spring and hence spring-test-mvc.

I will include relevant sections of my code to give you a feel for how I have structured it:

PurchaseController:

@Controller
public class PurchaseController
{    
    @Autowired
    private IPurchaseService purchaseService;

    @RequestMapping(value = "purchases", method = RequestMethod.GET)
    @ResponseBody
    public final List<Purchase> getAll()
    {
        return purchaseService.getAll();
    }
}

PurchaseService:

@Service
public class PurchaseService implements IPurchaseService
{
    @Autowired
    private IPurchaseDAO purchaseDAO;

    public PurchaseService()
    {

    }

    @Transactional
    public List<Purchase> getAll()
    {
        return purchaseDAO.findAll();
    }
}

PurchaseDAO:

@Repository
public class PurchaseDAO extends AbstractJpaDAO<Purchase> implements
        IPurchaseDAO
{

    @PersistenceContext
    EntityManager entityManager;

    public PurchaseDAO()
    {
        setClazz(Purchase.class);
    }
}

AbstractJpaDAO:

public abstract class AbstractJpaDAO<T extends Serializable> implements
        IAbstractJpaDAO<T> {

    private Class<T> clazz;

    @PersistenceContext
    EntityManager entityManager;

    public void setClazz(final Class<T> clazzToSet) {
        this.clazz = clazzToSet;
    }

    public List<T> findAll() {
        return entityManager.createQuery("from " + clazz.getName())
                .getResultList();
    }
}

Here is my controller test:

public class PurchaseControllerTest extends AbstractControllerTest
{
    @Autowired
    private IPurchaseService purchaseService;

    @Autowired
    private PurchaseController purchaseController;

    private MockMvc mockMvc;

    @Before
    public void setup()
    {
        // Test Purchase
        Purchase purchase = new Purchase();
        purchase.setPan(5412311111111121l);
        purchase.setCvc((short) 122);
        purchase.setExpiry("1215");
        purchase.setMerchantName("TestMerchant");
        purchase.setMerchantType("Airline");
        purchase.setTransactionAmount(new BigDecimal("300.99"));
        
        mockMvc = MockMvcBuilders.standaloneSetup(purchaseController).build();
        purchaseService.addPurchase(purchase);
    }

    @Test
    public void testGetAll() throws Exception
    {
        this.mockMvc
                .perform(get("/purchase").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().type(MediaType.APPLICATION_JSON));
    }
}

AbstractControllerTest:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ControllerTestConfig.class}, loader=AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
public class AbstractControllerTest
{
   
}

ControllerTestConfig:

@Configuration
@Profile("test")
public class ControllerTestConfig {
    
    @Bean
public PurchaseService purchaseService()
{
    return new PurchaseService();
}

@Bean
public PurchaseDAO purchaseDAO()
{
    return new PurchaseDAO();
}

@Bean
public EntityManagerFactory entityManager()
{
    return new LocalEntityManagerFactoryBean().getObject();
}
}



I get the following Error when running my test via Eclipse's JUnit:

2012-07-08 16:16:00,017 TRACE [main] o.s.c.s.GenericApplicationContext [AbstractApplicationContext.java:322] Publishing event in org.springframework.context.support.GenericApplicationContext@42b307f0: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.GenericApplicationContext@42b307f0: startup date [Sun Jul 08 16:15:59 IST 2012]; root of context hierarchy]
2012-07-08 16:16:00,018 DEBUG [main] o.s.t.c.TestContext [TestContext.java:150] Storing ApplicationContext for test class [class com.app.controller.PurchaseControllerTest] in cache under key [[MergedContextConfiguration@2f8a49e0 testClass = PurchaseControllerTest, locations = '{}', classes = '{class com.app.spring.testing.ControllerTestConfig}', activeProfiles = '{test}', contextLoader = 'org.springframework.test.context.support.AnnotationConfigContextLoader']].
2012-07-08 16:16:00,019 TRACE [main] o.s.b.CachedIntrospectionResults [CachedIntrospectionResults.java:222] Getting BeanInfo for class [com.app.controller.PurchaseControllerTest]
2012-07-08 16:16:00,022 TRACE [main] o.s.b.CachedIntrospectionResults [CachedIntrospectionResults.java:238] Caching PropertyDescriptors for class [com.app.controller.PurchaseControllerTest]
2012-07-08 16:16:00,022 TRACE [main] o.s.b.CachedIntrospectionResults [CachedIntrospectionResults.java:250] Found bean property 'class' of type [java.lang.Class]
2012-07-08 16:16:00,023 TRACE [main] o.s.b.CachedIntrospectionResults [CachedIntrospectionResults.java:250] Found bean property 'mockMvc' of type [org.springframework.test.web.server.MockMvc]
2012-07-08 16:16:00,026 DEBUG [main] o.s.b.f.a.InjectionMetadata [InjectionMetadata.java:60] Found injected element on class [com.app.controller.PurchaseControllerTest]: AutowiredFieldElement for private com.app.service.IPurchaseService com.app.controller.PurchaseControllerTest.purchaseService
2012-07-08 16:16:00,026 DEBUG [main] o.s.b.f.a.InjectionMetadata [InjectionMetadata.java:60] Found injected element on class [com.app.controller.PurchaseControllerTest]: AutowiredFieldElement for private com.app.controller.PurchaseController com.app.controller.PurchaseControllerTest.purchaseController
2012-07-08 16:16:00,027 DEBUG [main] o.s.b.f.a.InjectionMetadata [InjectionMetadata.java:85] Processing injected method of bean 'com.app.controller.PurchaseControllerTest': AutowiredFieldElement for private com.app.service.IPurchaseService com.app.controller.PurchaseControllerTest.purchaseService
2012-07-08 16:16:00,034 ERROR [main] o.s.t.c.TestContextManager [TestContextManager.java:324] Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@403ef810] to prepare test instance [com.app.controller.PurchaseControllerTest@656546ef]
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.app.controller.PurchaseControllerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.app.service.IPurchaseService com.app.controller.PurchaseControllerTest.purchaseService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.app.service.IPurchaseService] 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:287) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

Any ideas on why my PurchaseService is not created and injected?


UPDATE

So I added my Purchase Service Bean to ControllerTestConfig...now I get an error about my purchaseDAO.

How can I add this to my ControllerTestConfig? Does it have to be nested inside the Purchase Bean?

2012-07-08 18:30:23,029 ERROR [main] o.s.t.c.TestContextManager [TestContextManager.java:324] Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@56da6bf4] to prepare test instance [com.app.controller.PurchaseControllerTest@df4cbee]
java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) [junit-4.8.1.jar:na]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:290) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) [junit-4.8.1.jar:na]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236) [junit-4.8.1.jar:na]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) [.cp/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'purchaseService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.app.persistence.dao.IPurchaseDAO com.app.service.impl.PurchaseService.purchaseDAO; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'purchaseDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 0
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]

I added PurchaseDAO bean to ControllerTestConfig.

Update 2

So now I'm getting a null pointer exception after adding An EntityManagerFactory Bean to my ControllerTestConfig!

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) [junit-4.8.1.jar:na]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:290) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) [junit-4.8.1.jar:na]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) [junit-4.8.1.jar:na]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236) [junit-4.8.1.jar:na]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) [.cp/:na]
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) [.cp/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'purchaseService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.app.persistence.dao.IPurchaseDAO com.app.service.impl.PurchaseService.purchaseDAO; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'purchaseDAO': Injection of persistence dependencies failed; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]

Any ideas? Thanks

like image 610
Thomas Buckley Avatar asked Jul 08 '12 15:07

Thomas Buckley


People also ask

What is service and controller in spring?

The @Service annotation is used in your service layer, and @Controller is for Spring MVC controllers in your presentation layer. A @Controller typically would have a URL mapping and be triggered by a web request.

What is MockMVC used for?

MockMvc provides support for Spring MVC testing. It encapsulates all web application beans and makes them available for testing. We'll initialize the mockMvc object in the @BeforeEach annotated method, so that we don't have to initialize it inside every test.

What is MockMvcBuilders standaloneSetup?

The MockMvcBuilders. standaloneSetup allows to register one or more controllers without the need to use the full WebApplicationContext .


2 Answers

This will not work as spring-test-mvc does not pick the context configuration from the ContextConfiguration of the Junit, instead you can do this(assuming that your Service beans are being loaded up someplace through ControllerTestConfig configuration):

MockMvcBuilders.annotationConfigSetup(ControllerTestConfig.class).build().perform(get("/purchase").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().type(MediaType.APPLICATION_JSON));
like image 106
Biju Kunjummen Avatar answered Oct 24 '22 15:10

Biju Kunjummen


You will need to include some @Bean's in your test @Configuration profile.

Previously you had none, but since your update, I see that you have your PurchaseService added. Well, your PurchaseService has an @Autowired PurchaseDao which also needs to be added to your test profile.

Try adding this to ControllerTestConfig:

@Bean
public IPurchaseDao purchaseDao() {
    return new PurchaseDao();
}

Your PurchaseDao uses an EntityManager which may also need to be added to your ControllerTestConfig class. (I am not very familiar with this portion, so I can't say difinitively...)

like image 27
nicholas.hauschild Avatar answered Oct 24 '22 14:10

nicholas.hauschild