tutorial mvc mediante framework español ejemplo desarrollo aplicaciones spring spring-mvc junit spring-test

mediante - El servicio no se ha inyectado en la prueba del controlador Spring con spring-test-mvc



spring mvc 4 tutorial español (4)

Estoy intentando construir un conjunto de pruebas de unidad / integración para un servidor API Restful que he creado usando Spring MVC 3.1.1.

Estoy tratando de usar Spring-test-mvc .

Soy relativamente nuevo en Spring y por lo tanto en spring-test-mvc.

Incluiré secciones relevantes de mi código para darle una idea de cómo lo he estructurado:

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(); } }

CompraDAO:

@Repository public class PurchaseDAO extends AbstractJpaDAO<Purchase> implements IPurchaseDAO { @PersistenceContext EntityManager entityManager; public PurchaseDAO() { setClazz(Purchase.class); } }

ResumenJpaDAO:

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(); } }

Aquí está mi prueba de controlador:

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(); } }



Obtengo el siguiente error al ejecutar mi prueba a través de JUnit de Eclipse:

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]

¿Alguna idea sobre por qué mi PurchaseService no se crea ni se inyecta?


ACTUALIZAR

Así que agregué mi Bean de servicio de compra a ControllerTestConfig ... ahora recibo un error sobre mi purchaseDAO.

¿Cómo puedo agregar esto a mi ControllerTestConfig? ¿Tiene que estar anidado dentro del Bean de Compra?

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]

Agregué Bean PurchaseDAO a ControllerTestConfig.

Actualización 2

¡Así que ahora obtengo una excepción de puntero nulo después de agregar un EntityManagerFactory Bean a mi 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]

¿Algunas ideas? Gracias


Deberá incluir algunos @Bean en su perfil @Configuration test .

Anteriormente no tenía ninguno, pero desde su actualización, veo que tiene su PurchaseService agregado. Bueno, su PurchaseService tiene un @Autowired PurchaseDao que también debe agregarse a su perfil de test .

Intente agregar esto a ControllerTestConfig :

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

Su PurchaseDao utiliza un EntityManager que también puede ser necesario agregar a su clase ControllerTestConfig . (No estoy muy familiarizado con esta parte, así que no puedo decir de manera definitiva ...)


Esto no funcionará ya que spring-test-mvc no elige la configuración de contexto desde ContextConfiguration de Junit, en su lugar puede hacerlo (suponiendo que sus beans de servicio se carguen en algún lugar mediante la configuración de ControllerTestConfig):

MockMvcBuilders.annotationConfigSetup(ControllerTestConfig.class).build().perform(get("/purchase").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().type(MediaType.APPLICATION_JSON));


Dado que está utilizando @ContextConfiguration (el marco TestContext) para cargar la configuración de Spring, supongo que quiere hacer una prueba de integración que implique cargar sus Controladores, Servicios y Repositorios desde su configuración de Spring. Sin embargo, está utilizando el método standaloneSetup de MockMvcBuilders, que está destinado a pruebas unitarias más enfocadas que se dirigen principalmente a controladores (los servicios y repositorios generalmente se burlan).

Considere usar el método webApplicationContextSetup de MockMvcBuilders. Aquí hay un ejemplo .


No sé si esto lo ayudará, pero para heredar el contexto de @ContextConfiguration debe autoconectarse en la clase de prueba, y luego de eso, puede establecerlo como un contexto primario de su ControllerTestConfig.class .

En mi configuración, separé la configuración de persistencia para que se cargue desde @ContextConfiguration y la configuración de MVC se cargue desde annotationConfigSetup .

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(loader=AnnotationConfigContextLoader.class, classes={PersistenceJPAConfig.class}) public class PurchaseControllerTest { @Autowired private ApplicationContext ac; private PurchaseService service; private MockMvc mvc; @Before public void before() { mvc = annotationConfigSetup(ControllerTestConfig.class). setParentContext(ac).build(); service = ac.getBean(PurchaseService.class); } }

Como el annotationConfigSetup no sabe que tiene una configuración externa, debe indicarlo directamente estableciendo su contexto principal.

No puede insertar su servicio dentro de su clase de prueba, pero puede recuperarlo del contexto y también hacerlo inyectar en sus controladores. Bueno, puede inyectar, pero no será el mismo servicio que será inyectado en sus controladores. Obteniéndolo del contexto que pasas como padre, garantizas que son iguales. Como me burlo de mis servicios, esto es importante.