}
值得注意的是,对于final类、匿名类和Java的基本类型是无法进行Mock的。
设定Mock对象的期望值行为及返回值
有两种通用基础设定写法:
-
when(...).thenReturn(...);
-
doReturn(...).when(...).someMethod();
但是,doReturn(...).when(mockObj.someMethod());
会抛异常。
@Test
public void testMockClass() {
// 对方法设定返回值,也就是设置数据桩
when(mockServiceImpl.findUserByUserName(“tom”)).thenReturn(new User(“tom”, “1234”));
doReturn(true).when(mockServiceImpl).hasMatchUser(“tom”, “1234”);
User user = mockServiceImpl.findUserByUserName(“tom”);
boolean isMatch = mockServiceImpl.hasMatchUser(“tom”, “1234”);
assertNotNull(user);
assertEquals(user.getUserName(), “tom”);
assertEquals(isMatch, true);
}
也值得注意的是,static和final修饰的方法无法进行设定的。
验证交互行为
Mock对象一旦建立便会自动记录自己的交互行为,所以,可以有选择地对其交互行为进行验证。
@Test
// 模拟接口UserService测试
public void testMockInterface() {
// 对方法设定返回值,也就是设置数据桩
when(mockUserService.findUserByUserName(“tom”)).thenReturn(new User(“tom”, “1234”));
doReturn(true).when(mockUserService).hasMatchUser(“tom”, “1234”);
// 对void方法进行方法预期设定
User u = new User(“John”, “1234”);
doNothing().when(mockUserService).registerUser(u);
// 执行方法调用
User user = mockUserService.findUserByUserName(“tom”);
boolean isMatch = mockUserService.hasMatchUser(“tom”, “1234”);
mockUserService.registerUser(u);
assertNotNull(user);
assertEquals(user.getUserName(), “tom”);
assertEquals(isMatch, true);
// 验证交互行为
verify(mockUserService).findUserByUserName(“tom”);
// 验证方法只调用一次
verify(mockUserService, times(1)).findUserByUserName(“tom”);
// 验证方法至少调用一次
verify(mockUserService, atLeastOnce()).findUserByUserName(“tom”);
verify(mockUserService, atLeast(1)).findUserByUserName(“tom”);
// 验证方法至多调用一次
verify(mockUserService, atMost(1)).findUserByUserName(“tom”);
verify(mockUserService).hasMatchUser(“tom”, “1234”);
verify(mockUserService).registerUser(u);
}
同常主要Java Web应用分Controller,Service,DAO基本三层来进行开发。
接下来通过使用Mockito框架对Service进单元测试。
Domain领域对象:
public class Product {
}
DAO数据连接层:
public interface ProductDao {
int getAvailableProducts(Product product);
int orderProduct(Product product, int orderedQuantity);
}
Service业务逻辑层:
public class ProductService {
private ProductDao productDao;
public boolean buy(Product product, int orderedQuantity) {
int availableQuantity = productDao.getAvailableProducts(product);
if (orderedQuantity > availableQuantity) {
return false;
}
productDao.orderProduct(product, orderedQuantity);
return true;
}
}
Service测试用例:
public class ProductServiceTest {
private ProductDao productDao;
@Before
public void setupMock() {
//模拟Dao层
productDao = mock(ProductDao.class);
}
@Test
public void testBuy() {
int availableQuantity = 30;
Product product = new Product();
ProductService productService = new ProductService();
//设置数据桩
when(productDao.getAvailableProducts(product)).thenReturn(availableQuantity);
//doReturn(availableQuantity).when(productDao).getAvailableProducts(product);
//这写法不行
//doReturn(availableQuantity).when(productDao.getAvailableProducts(product));
//通过Spring测试框架提供的工具类为目标对象私有属性设值,这样就不用ProductDao另建setProductDao()方法
ReflectionTestUtils.setField(productService, “productDao”, productDao);
Assert.assertFalse(productService.buy(product, 31));
Assert.assertTrue(productService.buy(product, 3));
//验证交互行为
verify(productDao).orderProduct(product, 3);
verify(productDao, times(2)).getAvailableProducts(product);
}
}
测试用例中,用到Spring test框架的ReflectionTestUtils
,它可以为目标对象非公有属性设值,或调用非公有setter方法,方便测试过程中使用。
为了使用ReflectionTestUtils
,需要向pom.xml添加下面的依赖
org.springframework
spring-test
3.0.5.RELEASE
test
org.springframework
spring-core
3.0.5.RELEASE
test
commons-logging
commons-logging
1.2
test