如何在同一个 Mock 上对 return 不同的值进行两个方法调用?
How can I get two method calls on the same Mock to return different values?
我有一个我正在尝试模拟的函数,它包含 while 循环形式的递归逻辑,我正在尝试弄清楚如何在不永远循环的情况下访问 while 循环的内部部分。
//this is supposed to go through all the items in the grocery list given the parameters until the groceries are all checked out
public void checkOut(String maxItems, String code){
List<cereal> groceryList;
groceryList = groceryListDao.getList(String maxItems, String code);
while (!groceryList.isEmpty()){
groceryListDao.total();
//other logic
groceryList = groceryListDao.getList(String maxItems, String code);
}
}
我能够编写一个 junit 测试来验证如果购物清单为空,则永远不会进入 while 循环。但是,我不确定如何编写代码来测试是否进入了 while 循环,因为我需要模拟 groceryListDao.getList 不为空才能进入 while 循环,然后为空才能退出 while 循环。我不知道怎么做。
@Test
public void checkout() {
List<Cereal> cereal = new ArrayList<>();
Cereal x = new Cereal();
cereal.add(x);
when(groceryListDao.getList(anyString(), anyString())).thenReturn(cereal);
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1).total());
}
如何验证是否在循环内调用了 total() 而不会卡住?
您可以链接 thenReturn
,以便后续调用模拟 return 不同的东西:
public class GroceryServiceTest {
@Test
public void checkout() {
GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
GroceryService groceryService = new GroceryService(groceryListDao);
List<GroceryService.Cereal> cereal = new ArrayList<>();
GroceryService.Cereal x = new GroceryService.Cereal();
cereal.add(x);
// first return a list with one item, then an empty list
when(groceryListDao.getList(anyString(), anyString())).thenReturn(cereal).thenReturn(Collections.emptyList());
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1)).total();
}
}
这不是一个完美的测试,因为模拟会 return 一个空列表,而不会中间调用 total
。
您可以像这样模拟 DAO 的语义:
public class GroceryServiceTest {
@Test
public void checkout() {
GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
GroceryService groceryService = new GroceryService(groceryListDao);
List<GroceryService.Cereal> cereal = new ArrayList<>();
AtomicBoolean totalCalled = new AtomicBoolean(false);
GroceryService.Cereal x = new GroceryService.Cereal();
cereal.add(x);
when(groceryListDao.getList(anyString(), anyString())).thenAnswer(new Answer<List<GroceryService.Cereal>>() {
@Override
public List<GroceryService.Cereal> answer(InvocationOnMock invocationOnMock) throws Throwable {
if (totalCalled.get()) {
return Collections.emptyList();
} else {
return cereal;
}
}
});
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
totalCalled.set(true);
return null;
}
}).when(groceryListDao).total();
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1)).total();
}
}
为了完整起见,这里是 GroceryService
代码:
import java.util.List;
public class GroceryService {
final GroceryService.GroceryListDao groceryListDao;
public GroceryService(GroceryService.GroceryListDao groceryListDao) {
this.groceryListDao = groceryListDao;
}
interface GroceryListDao {
List<Cereal> getList(String maxItems, String code);
void total();
}
static class Cereal {}
public void checkout(String maxItems, String code){
List<Cereal> groceryList;
groceryList = groceryListDao.getList(maxItems, code);
while (!groceryList.isEmpty()){
groceryListDao.total();
//other logic
groceryList = groceryListDao.getList(maxItems, code);
}
}
}
我有一个我正在尝试模拟的函数,它包含 while 循环形式的递归逻辑,我正在尝试弄清楚如何在不永远循环的情况下访问 while 循环的内部部分。
//this is supposed to go through all the items in the grocery list given the parameters until the groceries are all checked out
public void checkOut(String maxItems, String code){
List<cereal> groceryList;
groceryList = groceryListDao.getList(String maxItems, String code);
while (!groceryList.isEmpty()){
groceryListDao.total();
//other logic
groceryList = groceryListDao.getList(String maxItems, String code);
}
}
我能够编写一个 junit 测试来验证如果购物清单为空,则永远不会进入 while 循环。但是,我不确定如何编写代码来测试是否进入了 while 循环,因为我需要模拟 groceryListDao.getList 不为空才能进入 while 循环,然后为空才能退出 while 循环。我不知道怎么做。
@Test
public void checkout() {
List<Cereal> cereal = new ArrayList<>();
Cereal x = new Cereal();
cereal.add(x);
when(groceryListDao.getList(anyString(), anyString())).thenReturn(cereal);
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1).total());
}
如何验证是否在循环内调用了 total() 而不会卡住?
您可以链接 thenReturn
,以便后续调用模拟 return 不同的东西:
public class GroceryServiceTest {
@Test
public void checkout() {
GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
GroceryService groceryService = new GroceryService(groceryListDao);
List<GroceryService.Cereal> cereal = new ArrayList<>();
GroceryService.Cereal x = new GroceryService.Cereal();
cereal.add(x);
// first return a list with one item, then an empty list
when(groceryListDao.getList(anyString(), anyString())).thenReturn(cereal).thenReturn(Collections.emptyList());
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1)).total();
}
}
这不是一个完美的测试,因为模拟会 return 一个空列表,而不会中间调用 total
。
您可以像这样模拟 DAO 的语义:
public class GroceryServiceTest {
@Test
public void checkout() {
GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
GroceryService groceryService = new GroceryService(groceryListDao);
List<GroceryService.Cereal> cereal = new ArrayList<>();
AtomicBoolean totalCalled = new AtomicBoolean(false);
GroceryService.Cereal x = new GroceryService.Cereal();
cereal.add(x);
when(groceryListDao.getList(anyString(), anyString())).thenAnswer(new Answer<List<GroceryService.Cereal>>() {
@Override
public List<GroceryService.Cereal> answer(InvocationOnMock invocationOnMock) throws Throwable {
if (totalCalled.get()) {
return Collections.emptyList();
} else {
return cereal;
}
}
});
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
totalCalled.set(true);
return null;
}
}).when(groceryListDao).total();
groceryService.checkout("10", "A5ALV350IIXL");
verify(groceryListDao, times(1)).total();
}
}
为了完整起见,这里是 GroceryService
代码:
import java.util.List;
public class GroceryService {
final GroceryService.GroceryListDao groceryListDao;
public GroceryService(GroceryService.GroceryListDao groceryListDao) {
this.groceryListDao = groceryListDao;
}
interface GroceryListDao {
List<Cereal> getList(String maxItems, String code);
void total();
}
static class Cereal {}
public void checkout(String maxItems, String code){
List<Cereal> groceryList;
groceryList = groceryListDao.getList(maxItems, code);
while (!groceryList.isEmpty()){
groceryListDao.total();
//other logic
groceryList = groceryListDao.getList(maxItems, code);
}
}
}