I'm trying to test my Spring web app but i have some problems.
I added a test class in my maven
@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testName() throws Exception {
List<UserEntity> userEntities = userService.getAllUsers();
Assert.assertNotNull(userEntities);
}
}
But i got a NullPointerException
on userService when i try to run this test.
IntelliJ say "Could not autowire. No beans of 'UserService' type found.
After adding @RunWith(SpringJUnit4ClassRunner.class)
, i got this exception
java.lang.IllegalStateException: Neither GenericXmlContextLoader nor AnnotationConfigContextLoader was able to detect defaults, and no ApplicationContextInitializers were declared for context configuration
How can i solve it ? And i think i need to run this test on my tomcat server but how can i deploy for testing with IntelliJ ? (like command 'mvn clean install tomcat7:run-war-only')
You have to provide the location of your Spring context file to be initialized before the test starts.
Test class
@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration(locations = { "classpath:META-INF/your-spring-context.xml" })
public class UserServiceTest extends AbstractJUnit4SpringContextTests {
@Autowired
private UserService userService;
@Test
public void testName() throws Exception {
List<UserEntity> userEntities = userService.getAllUsers();
Assert.assertNotNull(userEntities);
}
}
your-spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="userService" class="java.package.UserServiceImpl"/>
</beans>
Depending on what you are trying to achieve via testing, you need to either write a unit test or a Spring integration test.
In the latter case you need to change your code to:
@ContextConfiguration(locations = {"classpath:/applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class UserServiceTest {
@Autowired
private UserService userService;
//test whatever
}
When performing such integration tests, you will probably want to hook into the Spring Profile mechanism. By leveraging it, you will be able to reuse the same configuration you use in your production code while selectively replacing certain beans (for example by swapping the production datasource with an in-memory database).
If you are using Spring 4, then Conditional would give you even more flexibility.
I suggest you give this part of the Spring test documentation a good read to get an overview of what Spring integration tests can do for you.
The case you have presented I'm not sure warrants itself for an integration test (more details on what UserService
actually does would give a clearer picture).
For example if UserService
uses a UserDao
and then performs some custom logic on the results from the dao, creating a unit test for UserService
with a mocked UserDao
would be a good option.
If you are using constructor injection to supply the dao to the service, then construction of the service is a no-brainer. If you are using field injection via @Autowired
(which you shouldn't be if you can avoid it), then you would need to inject the mock via reflection. A super utility for that is Spring's ReflectionTestUtils
Then you could create an integration test for UserDao
which would test it's ability to correctly read data from the database.
Finally note that the comments above are independent of the how you run the tests (IDE or Maven). If it's configured correctly, the tools will happilly run it
I am not sure, but think that you need to add locations property to ContextConfiguration annotation where you could specify the xml file where all of the beans exist. Moreover, I guess the operation of fetching all users is database related so it is good to add TransactionConfiguration annotation. Thus, you should have something like this:
@ContextConfiguration(locations = {"classpath:spring-config-junit.xml"})
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)