We are using @Autowired (by Type)
for dependency management of our multi-layer/multi-tiered Spring 3
project. Here is how our interface/implementation look,
public interface UserProfileService {
public abstract UserProfileDO
getUser(String userId) throws UserNotFoundException;
}
@Service
public class
UserProfileServiceImpl extends BaseService implements UserProfileService,
InitializingBean
{
@Autowired
private UserProfileDAO userprofileDao;
@Override
public void afterPropertiesSet()
throws Exception
{
Assert.notNull(userprofileDao, "UserProfileDAO
not injected.");
}
@Override
public UserProfileDO
getUser(String userId) throws UserNotFoundException
{
IMPersonEntity person;
try
{
person = userprofileDao.getUser(userId);
}
catch (DataAccessException
e)
{
throw new UserNotFoundException(userId);
}
if (person
== null)
throw new UserNotFoundException(userId);
UserProfileDO userProfile = (UserProfileDO) objectConversion(person,
UserProfileDO.class);
return userProfile;
}
public void setUserprofileDao(UserProfileDAO
userprofileDao)
{
this.userprofileDao =
userprofileDao;
}
}
UserProfileService has two dependent
beans
·
UserProfileDAO: Needs to be
mocked. This is a data-access layer which is implementated using IBMs Tivoli APIs and LDAP.
·
dozerObjectMapper: This dependency is from the BaseService, which converts
the Entities into DomainObject. We don’t intend to mock this.
To configure Mockitto, I started by using @Mock annotation but in my test I needed a mix of @Autowired and @Mock. I don’t think MockitoJUnit44Runner loads the
beans from ContextConfiguration. Also, I didn’t find any special value on using @Mock
annotation (This is my first mockitto test, maybe I will realize its value later).
@RunWith(MockitoJUnit44Runner.class)
@ContextConfiguration("classpath:mock-userprofile-service-context.xml")
public class UserProfileServiceTest implements InitializingBean
{
@Autowired private UserProfileService userDetailsService;
@Mock
private UserProfileDAO mockUserDetailsDao;
Other issue with my test was that my ServiceInterface
(UserProfileService) did not have setter for DAO ( as per desing), that means I cannot set my mockObject on the service. I overcame this
problem by using (abusing) Reflection.
So, I changed my test to switched back to SpringJUnit4ClassRunner
(removed
@Mock annotation) and I used reflection to set mockObject to the testService.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:mock-userprofile-service-context.xml")
public class UserProfileServiceTest implements InitializingBean
{
@Autowired
private UserProfileService userDetailsService;
private UserProfileDAO mockUserDetailsDao
= mock(UserProfileDAO);
@Test
public void getValidUser() throws UserNotFoundException,
EntityNotFoundException, MultipleEntitiesFoundException
{
IMPerson mockPerson = new IMPerson();
mockPerson.setDisplayName("test cca");
when(mockUserDetailsDao.getUser("cca_test_user")).thenReturn(mockPerson);
ReflectionTestUtils.setField(userDetailsService,
"userProfileDAO", mockUserDetailsDao);
UserProfileDO user = userDetailsService.getUser("cca_test_user");
assertNotNull(user);
Assert.assertEquals("test cca", user.getDisplayName());
}
This got me going, I could run tests
with mock objects. I still wanted to get rid of reflection and find a way
to Autowire mockObject (maintain consistency across application).
So, I moved my mockbean creation to test
context and had @Autowired in service detect the required mock Dao (no more
Reflection). I still needed a reference to mock bean in my test, to configure mock conditions.
This is how my final test context
looks, with mock bean creation.
mock-userprofile-service-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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
<bean id="mockUserProfileDao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.company.dataaccess.userprofile.dao.UserProfileDAO"
/>
</bean>
<bean class="com.company.service.userprofile.UserProfileServiceImpl"/>
<bean class="org.dozer.DozerBeanMapper"/>
</beans>
This is how my final test looks
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:mock-userprofile-service-context.xml")
public class UserProfileServiceTest
{
@Autowired
private UserProfileService userDetailsService;
@Autowired
private UserProfileDAO mockUserDetailsDao;
@Test
public void getValidUser() throws UserNotFoundException,
EntityNotFoundException, MultipleEntitiesFoundException
{
IMPerson mockPerson = new IMPerson();
mockPerson.setDisplayName("test cca");
when(mockUserDetailsDao.getUser("cca_test_user")).thenReturn(mockPerson);
UserProfileDO user = userDetailsService.getUser("cca_test_user");
assertNotNull(user);
Assert.assertEquals("test cca", user.getDisplayName());
}
@Test
@ExpectedException(UserNotFoundException.class)
public void getMultipleInValidUser() throws UserNotFoundException,
EntityNotFoundException, MultipleEntitiesFoundException
{
when(mockUserDetailsDao.getUser("multipleUserids")).thenThrow(new MultipleEntitiesFoundException());
userDetailsService.getUser("multipleUserids");
fail();
}
@Test
@ExpectedException(UserNotFoundException.class)
public void getInValidUser() throws UserNotFoundException,
EntityNotFoundException, MultipleEntitiesFoundException
{
when(mockUserDetailsDao.getUser("nulluser")).thenReturn(null);
userDetailsService.getUser("nulluser");
fail();
}
}
Happy testing J