Spring + Mockito test injection
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:
@Testpublic 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.
You have three options to set your mock dao:
- Test the implementation - which gives a seam for your mock via the setDao method. (as JB's answer)
- Add the setDao method to the interface - not desired since you don't want to add code just to support your tests.
- Add a constructor to the impl class to accept the dao - not desired for same reason as #2.
If you wanted to do #3, you'll need to add a constructor to the MobileService that accepts the MobileDao.
public MobileServiceImpl(MobileDao mobileDao) { this.mobileDao = mobileDao;}
Then your test will look like this:
import static org.mockito.Mockito.verify;import static org.mockito.Mockito.*;import java.util.Date;import org.junit.Before;import org.junit.Test;public class MobileServiceImplTest { private MobileService systemUnderTest; private MobileDao mobileDao; @Before public void setup() { mobileDao = mock(MobileDao.class); systemUnderTest = new MobileServiceImpl(mobileDao); } @Test public void testGetUser() { //if you need to, configure mock behavior here. //i.e. when(mobileDao.someMethod(someObject)).thenReturn(someResponse); systemUnderTest.getUser("accessCode", new Date()); verify(mobileDao).getUser("JeffAtwood"); }}
Please note that you have not provided us with the details of the MobileDao so I created a getUser method that accepts a String.
To make the test pass, your MobileServiceImpl would just need this:
mobileDao.getUser("JeffAtwood");