My question is very similar to the issue raised in Injecting Mockito mocks into a Spring bean. In fact, I believe the accepted answer there might actually work for me. However, I've got one issue with the answer, and then some further explanation in case the answer there is not in fact my answer.
So I followed the link in the aforementioned post to the Springockito website. I altered my test-config.xml
to include something similar to the following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mockito.org/spring/mockito http://www.mockito.org/spring/mockito.xsd">
...
<mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />
...
</beans>
There seems to be something wrong with the www.mockito.org
redirect currently, so I found the XSD code at https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd and altered the final entry in xsi:schemaLocation to point to this bitbucket link.
Running mvn test
then produced the following error (newlines added for readability):
Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 43 in XML document from class path resource [spring/test-context.xml] is invalid;
nested exception is org.xml.sax.SAXParseException; lineNumber: 43; columnNumber: 91;
The prefix "mockito" for element "mockito:mock" is not bound.
So the question regarding Springockito is: Is it possible anymore to include this? What am I missing?
Now, on to the further explanation...
I have an interface whose implementation I'm trying to test:
public interface MobileService {
public Login login(Login login);
public User getUser(String accessCode, Date birthDate);
}
The implementation contains a DAO that Spring @Autowire
s in for me:
@Service
public class MobileServiceImpl implements MobileService {
private MobileDao mobileDao;
@Autowired
public void setMobileDao(MobileDao mobileDao) {
this.mobileDao = mobileDao;
}
}
I don't want to alter my interface to include a setMobileDao
method, because that would be adding code just to support my unit testing. I'm trying to mock out the DAO since the actual SUT here is the ServiceImpl. How can I achieve this?
You don't want to test your interface: it contains no code at all. You want to test your implementation. So the setter is available. Just use it:
@Test
public void testLogin() {
MobileServiceImpl toTest = new MobileServiceImpl();
toTest.setMobileDao(mockMobileDao);
// TODO call the login method and check that it works as expected.
}
No need for a spring context. Just instanciate your POJO service, inject mock dependencies manually, and test the methods you want to test.
After struggling with the Springockito XSD issue, for a while, I found a much simpler solution. Let Spring inject the mock for you using a factory method, i.e. in applicationContext.xml put:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.gerrydevstory.mycoolbank.AccountsDAO"/>
</bean>
<bean class="com.gerrydevstory.mycoolbank.BankingService"/>
</beans>
where the AccountsDAO bean is injected into the BankingService class. The corresponding JUnit test case is:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/BankingServiceTest.xml")
public class BankingServiceTest {
@Autowired private BankingService bankingService;
@Autowired private AccountsDAO mockAccountsDAO;
@Test
public void testTransfer() throws Exception {
// Setup 2 accounts
Account acc1 = new Account();
acc1.setBalance(800.00);
Account acc2 = new Account();
acc2.setBalance(200.00);
// Tell mock DAO to return above accounts when 1011 or 2041 is queried respectively
when(mockAccountsDAO.findById(1011)).thenReturn(acc1);
when(mockAccountsDAO.findById(2041)).thenReturn(acc2);
// Invoke the method to test
bankingService.transfer(1011, 2041, 500.00);
// Verify the money has been transferred
assertEquals(300.00, acc1.getBalance(), 0.001);
assertEquals(700.00, acc2.getBalance(), 0.001);
}
}
Personally I find this very elegant and easy to understand. For more details, see the original blog post.
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