Our typical business service with a cacheable method, as shown below:
@Service public class AccountService{ @Autowired public AccountDao accountDao; @Cacheable(value = "accounts") public Account getAccount(int accountId) { return accountDao.getObjectById(accountId); } }Primary concerns for this kind of service method unit test.
@Mock private AccountDao dao; @InjectMocks @Autowired private AccountService accountService; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); }Concern 2: Mockito and Spring proxies, the critical part is our method annotated with @Cacheable, then totally a different story now.
import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.util.ReflectionTestUtils; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath*:spring.xml" }) @ActiveProfiles("test") public class AccountCacheTest { @Mock private AccountDao dao; @InjectMocks @Autowired private AccountService accountService; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); AccountService bas = (AccountService) unwrapProxy(accountService); ReflectionTestUtils.setField(bas, "accountDao", dao); } @Test public void testAccoutCache() throws Exception { /* Arrange. */ Account acc1 = new Account(); acc1.setId(1); when(dao.getObjectById(1)).thenReturn(acc1, acc1); /* Act. */ accountService.getAccount(1); accountService.getAccount(1); /* Assert. */ verify(dao, times(1)).getObjectById(1); } public static final Object unwrapProxy(Object bean) throws Exception { /* * If the given object is a proxy, set the return value as the object * being proxied, otherwise return the given object. */ if (AopUtils.isAopProxy(bean) && bean instanceof Advised) { Advised advised = (Advised) bean; bean = advised.getTargetSource().getTarget(); } return bean; } }
LESSON: When test cache, we'd better utilize the assertSame.
1. http://kim.saabye-pedersen.org/2012/12/mockito-and-spring-proxies.html
2. http://www.captaindebug.com/2012/09/spring-31-caching-and-cacheable.html#.UmDsiflHK8s
http://www.captaindebug.com/2012/09/spring-31-caching-and-cacheevict.html#.UmDhNflHLQI