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?
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.
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
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.
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.
The MockMvcBuilders. standaloneSetup allows to register one or more controllers without the need to use the full WebApplicationContext .
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));
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...)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With