您的位置:首页 > 编程语言 > Java开发

Spring Cache Unit Test

2013-10-15 17:08 537 查看
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.

Concern 1: combine SpringJUnit4ClassRunner and Mockito

Solution: For common scenarios, we can apply the standard solution:

@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.

Solution: As we applied @Cacheable annotation(another annotations like @Transactional has same effect), Spring would generate a proxy automatically, thus direct @InjectMocks @Autowired does not work. Reference 1 taught me the following approach and it works.

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.

Reference

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: